开篇
2023 已经过去了,这一年没做啥事情,感觉最难忘就是这个奇迹项目,公司项目名字不叫奇迹,我也不知道该叫啥,业务是一个海外代充,类似小电商平台,里面商品分类、 SKU、购物车、订单、购买等功能,我觉得这玩意有人用那真是一个奇迹,所以就叫它奇迹代充吧。
项目技术栈选择
这个项目在公司后端由是 java 写的,在这里相当于是从零开始搭建一个 api 接口平台,屏蔽一些公司相关的东西,界面稍微改改。本系列我将系列总结一下 nestjs 开发经验以及 nextjs 和 angular 相关体验总结。
为什么要用 Nx

Nx 是一个 Monorepos Tools 的 CLI。底层架构设计来自 Angular-cli,为什么需要在 angular-cli 之上扩展出来一个新工具,那是因为 angular-cli 想要扩展一下功能,繁琐且不友好。然而 Nx 出现就轻松解决这个问题,灵活自如。正是验证那句古话:青出于蓝而胜于蓝。
我曾经按照 Angular-cli 底层设计模式,搭建了一个推广页的 Monorepos 的 CLI 管理器,也介绍怎么使用 Nx 更容易的实现这个 CLI 工具,并将思路和实现整理成文章,不知道为什么一直审核不通过。

Nx 它是一个 CLI 工具箱,为它支持技术栈提供最佳实践的工具链支持。
Nx 初衷是因为开发人员很难配置、维护、特别是集成各种工具和框架。建立一个既适用于少数开发人员,同时又能轻松扩展到整个组织的系统是很困难的。这包括设置低级构建工具、配置快速 CI,以及保持代码库健康、最新和可维护。
NX 以模块化方式构建,可以选择所需的功能。
Nx底层包提供了与技术无关的基本功能,例如:工作空间依赖分析、任务运行、缓存、并发、代码生成和代码自动迁移。Plugins是在NX底层包提供的基本功能之上构建的NPM软件包。NX插件包含代码生成器,执行者(抽象下层构建工具)和自动代码迁移,以使你的工具保持最新。Plugins通常是特定于技术的。例如,@nx/react增加了对构建React Apps和libs的支持, 最近版本添加的@nx/vue添加了使用Vue对构建Vue Apps和libs的支持。Plugins 消除了不同工具之间集成的障碍,并提供了保持工具更新的实用程序,从而提高了开发人员的工作效率。Nx团队维护着React,Next,Remix,Angular,Jest,Cypress,Storybook等80 多个社区插件。你可以使用 @nx/plugin 包轻松地搭建一个新插件,实现自动化本地工作区。Devkit是一组用于构建 Nx 插件的实用工具(底层就是使用 angular-devkit 封装)。Nx Cloud通过添加远程缓存和分布式任务执行,帮助你在CI上扩展项目。它还通过与GitHub、GitLab和BitBucket集成并提供可搜索的结构化日志来改善开发人员的工作效率Nx Console是VS Code,IntelliJ和VIM的扩展。它提供了代码自动完成、交互式生成器、工作空间可视化、强大的重构等功能。VS Code推荐的插件里面有。
回到为什么要用 Nx,因为我要用各种技术栈,想要把它们组织起来很难的一件事,有了 Nx 就让这一切变得迎刃而解,它还能自定义插件,让我的开发就更灵活自由。
为什么选择 Next
项目要求是需要 SEO 推广。
为了满足这个要求,那肯定需要服务端渲染,一开始的想法是 Nodejs(Express + ejs)就可以直接实现,一旦项目大了,页面交互多了,打包构建就相当麻烦。曾经实践过 nest mvc,借助 nx 实现前端打包。
拿到需求后,大家都很懵逼,老板提供了 3 个参考网站,仔细研究了一下这 3 个网站功能,整理了文档,画了一个大概草图。分好工作就要开始干活了。
我发现这些参考,其中 2 个是 Nuxt,一个是 PHP + JQ。
我不会 Nuxt 呀,但我会 Next。
就这样愉快的使用 Next,并且使用的是 app 路由,而不是 page,这 2 种路由模式是有点区别的,整体来说 app 比 page 开发体验更友好,缺点就是有些特定的第三方依赖库不好找。
为什么选择 Nest 和 Angular
用 Nest 和 Angular 不用换脑子,这 2 个写法类似,前者灵感来源后者。我一直使用这 2 个技术栈做开发,比如帮运维开发一个域名管理系统和路由器管理项目,都是用这 2 个完成的。
Nx里面以前版本初始化全栈工程默认选项就是Nest和Angular。
项目准备
我本人是 Windows 电脑,尽推荐 Windows 相关的工具,如果你是 Mac,可以自行选择。
- 开发工具
 
对于 node.js, typescript 前端等技术最好的开发工具毋庸置疑的就是 vscode。
Windows 推荐使用 cmder,和 vscode 很好集成。
项目已经自带推荐插件,建议启用。
- 使用 
nvm安装node.js 
- 非 Windows 版 github.com/nvm-sh/nvm
 - Windows 版 github.com/coreybutler...
 
项目版本要求:
            
            
              bash
              
              
            
          
          node -v
# 20+
        如果对你自己网络不自信可以使用淘宝镜像:
            
            
              bash
              
              
            
          
          npm config set registry http://registry.npmmirror.com/
        安装 pnpm 包管理
            
            
              bash
              
              
            
          
          npm install -g pnpm 
        配置pnpm淘宝镜像:
            
            
              bash
              
              
            
          
          pnpm config set registry https://registry.npmmirror.com/
        说明:nx 和 pnpm 本身是有思想冲突的。nx 提倡是一锅出,用多 lib 方式降低 app 复杂度,可以共享多个 app。这时候多个 app 和 lib 共享一份依赖。如果有不同的 app 项目(比如:react,vue,angular)在一个工作区里,就会出现冲突,我一直用 npm,最近才用 pnpm。减少每次安装包就需要使用
npm install --force xxx烦恼。
- 安装 
docker 
为了确保在不同的开发、测试和部署环境中都能保持一致。使团队成员能够更轻松地共享和重现开发环境,推荐使用 docker。
可以根据自己环境安装客户端。
Windows 强烈推荐使用 wsl2
检查 docker 和 docker-compose 版本,确保安装成功
项目已提供 docker-compose.yml 文件,直接执行:
            
            
              sh
              
              
            
          
          docker-compose up -d
        - 数据库图形化界面
 
根据自己喜欢需求选择喜欢的软件或工具。
vscode 插件里推荐 2 个数据库管理界面:
- DATABASE: 连接 
mysql - NoSql: 连接 
redis 
- 接口文档与测试
 
接口使用 swagger 文档,简单测试可以使用命令行 curl 工具或者 vscode 的 REST Client 插件。
创建工作区
            
            
              bash
              
              
            
          
          pnpm dlx create-nx-workspace@latest --name=miracle1 --pm=pnpm --preset=ts --ci=github --workspaceType=integrate
        - name 项目名,工作区目录
 - pm 使用 pnpm 包管理
 - preset 预设 ts 工作区
 - ci 帮我们创建一个 github workflows 文件
 - workspaceType 工作区类型,这里是一个大杂烩
 
等待它默默安装...
代码规范化
prettier
nx 初始化项目以后会自动添加 .prettierrc 和 .editorconfig
可以根据自己需求配置 prettier。这里不展开细说。
Eslint
            
            
              bash
              
              
            
          
          pnpm add -D @nx/eslint @nx/eslint-plugin jsonc-eslint-parser @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint eslint-config-prettier eslint-plugin-import
        创建 .eslintignore 文件:
            
            
              text
              
              
            
          
          node_modules
        创建 .eslintignore 文件:
            
            
              json
              
              
            
          
          {
  "root": true,
  "ignorePatterns": [
    "**/*"
  ],
  "plugins": [
    "@nx"
  ],
  "overrides": [
    {
      "files": "*.json",
      "parser": "jsonc-eslint-parser",
      "rules": {}
    },
    {
      "files": [
        "*.ts",
        "*.tsx",
        "*.js",
        "*.jsx"
      ],
      "rules": {
        "@nx/enforce-module-boundaries": [
          "error",
          {
            "enforceBuildableLibDependency": true,
            "allow": [],
            "depConstraints": [
              {
                "sourceTag": "*",
                "onlyDependOnLibsWithTags": [
                  "*"
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "files": [
        "*.ts",
        "*.tsx"
      ],
      "extends": [
        "plugin:@nx/typescript"
      ],
      "rules": {}
    },
    {
      "files": [
        "*.js",
        "*.jsx"
      ],
      "extends": [
        "plugin:@nx/javascript"
      ],
      "rules": {}
    },
    {
      "files": [
        "*.spec.ts",
        "*.spec.tsx",
        "*.spec.js",
        "*.spec.jsx"
      ],
      "env": {
        "jest": true
      },
      "rules": {}
    }
  ]
}
        在 nx.json 配置:
            
            
              json
              
              
            
          
          {
   ...
  "plugins": [
    {
      "plugin": "@nx/eslint/plugin",
      "options": {
        "targetName": "lint"
      }
    }
  ]
}
        
@nx/enforce-module-boundaries规则就是@nx/eslint-plugin里的功能。
enforce-module-boundaries 主要是限制包引用关系。可以使用 tags 来管理。配置文档,
@nx/eslint 会帮我们执行 eslint 检查:
            
            
              bash
              
              
            
          
          pnpm exec nx lint project 
        如果你觉得当前规则不满足需求,可以自己写一些规则,执行命令:
            
            
              bash
              
              
            
          
          pnpm exec nx generate @nx/eslint:workspace-rule --name=rule-name --no-interactive
        它会帮你生成一个 eslint rule 模板,并自动导入根配置里。
stylelint
写前端界面,如果需要写样式肯定少不了 stylelint。
初始化:
            
            
              bash
              
              
            
          
          pnpm create stylelint
        我们主要使用 scss:
            
            
              bash
              
              
            
          
          pnpm add -D stylelint-order stylelint-config-standard-scss
        打开 .stylelintrc.json 文件,把 stylelint-config-standard 替换为 stylelint-config-standard-scss,如下配置:
            
            
              json
              
              
            
          
          {
  "extends": [
    "stylelint-config-standard-scss"
  ],
  "plugins": [
    "stylelint-order"
  ],
  // 自定义覆盖规则
  "rules": {}
}
        
stylelint-order对css属性进行排序。
对于项目我们可以使用 nx-stylelint 可以帮助我们设置不同的项目:
            
            
              bash
              
              
            
          
          pnpm exec nx stylelint project 
        这里不详细展开,后面项目配置在详细讲解
commitlint
现在规范化 commit.message 算是一种主流。
            
            
              bash
              
              
            
          
          pnpm add -D @commitlint/cli @commitlint/config-conventional
        创建配置文件 commitlint.config.js:
            
            
              js
              
              
            
          
          module.exports = {
  extends: ['@commitlint/config-conventional']
}
        通用的书写模板:
            
            
              text
              
              
            
          
          type(scope?): subject 
body? 
footer?
        - type:类型
- feat:新增功能
 - fix:bug 修复
 - docs:文档更新
 - style:风格修复(现在可以使用lint-staged代替)
 - refactor:重构代码(既没有新增功能,也没有修复bug)
 - perf:改进性能、体验优化的代码更改
 - test:新增测试或更新现有测试用例
 - build:主要目的是修改项目构建系统或项目配置文件提交
 - ci:主要目的是修改项目继续集成流程配置文件提交
 - revert:回滚某个更早之前的提交
 - release:版本发布
 - chore:不属于以上类型的其他类型
 
 - scope:范围
 - subject:简单描述
 - body:更多内容
 - footer:
BREAKING CHANGE破坏变更信息 
举例:
- 更新 nx 依赖包升级:
 
            
            
              bash
              
              
            
          
          git commit -m "chore(dev-deps): bump @nx from 18.0.7 to 18.1.0"
        - 发布版本:
 
            
            
              bash
              
              
            
          
          git commit -m "release: bump the next branch to v18.2.0-next.0"
# 或者
git commit -m "chore(release): bump the next branch to v18.2.0-next.0"
        lint-staged
让我们在提交之前使用 lint-stage 来运行 lint 和格式化。
            
            
              bash
              
              
            
          
          pnpm add -D lint-staged
        创建配置文件 lint-staged.config.js:
            
            
              js
              
              
            
          
          module.exports = {
  "{apps,libs,tools}/**/*.ts": [
    "npx nx affected:lint --uncommitted  --parallel --fix --files"
  ],
  "*": [
    "npx nx format:write --uncommitted --files"
  ],
  "{apps,libs}/**/*.scss": [
    "npx stylelint --fix"
  ]
}
        - apps: 项目目录
 - libs:共享目录
 - tools:工具目录(包含部署脚本,nx自定义插件,自定义 eslint 规则等工具)
 
Nx 会自动为 nx format 和 nx lint 命令设置相应的 prettier 和 eslint,所以我们不需要过多的配置。
husky
我们有 commitlint 和 lint-staged,要想它们更好发挥作用还需要使用 husky。
husky 主要功能自动检测 git 提交消息、代码,并在提交或推送时运行 git 钩子。
安装:
            
            
              bash
              
              
            
          
          pnpm add -D husky
        初始化:
            
            
              bash
              
              
            
          
          pnpm exec husky init
        生成 2 个 git 钩子脚本:
pre-commit:git commit 时启用 lint-staged
默认会给我们生成一个 pre-commit 文件:
            
            
              sh
              
              
            
          
          #!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install lint-staged
        commit-msg:使用 commitlint 检查 commit.message 信息是否符合规范
创建一个 commit-msg 文件:
            
            
              sh
              
              
            
          
          #!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install -- commitlint --edit
        commitizen
一切都准备就绪,可以提交代码了,虽然 commitlint 那套规范看起来很酷,每次输入那么多需要记忆太多,我们可以使用 commitizen 来开启交互式的提交,当然你也可以使用 vscode 插件(推荐插件已提供)。
            
            
              bash
              
              
            
          
          pnpm add -D cz-customizable
        在 package.json 中添加一个新脚本:
            
            
              json
              
              
            
          
          "scripts": {
    ...,
    "commit": "./node_modules/cz-customizable/standalone.js"
}
        在工作区根目录,创建一个 .cz-config.js 文件。
里面内容如何编写可以参考 EXAMPLE,根据自己需求定制。
常用定制:
- types:根据自己需求选择更合理的分类
 - scopes:对你工作区项目分类,适合对生成版本日志有帮助
 - messages:提示语,你如果对英文不感冒,那么可以自定义中文
 
其他按注释可以自行选择。
配置了 types 和 scopes 可以更好使用 commitlint 的规则限制:
            
            
              js
              
              
            
          
          const { types, scopes } = require('./.cz-config.js');
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'header-max-length': [2, 'always', 100],
    /**
     * scope-enum 提交 scope 的枚举
     */
    'scope-enum': [2, 'always', scopes.map((s) => s.name)],
    /**
     * type-enum 提交的类型枚举
     */
    'type-enum': [2, 'always', types.map((t) => t.value)],
  },
};
        当你准备 git commit 时,允许命令:
            
            
              bash
              
              
            
          
          pnpm run commit
        到这里我们就可以 git push 我们的代码了。
接下来我们会介绍初始化 3 个项目以及基本配置和一些开发说明。