# 项目规范最佳实践

在开发项目的过程中，会涉及到多人协作共同开发，由于每个人的个人习惯不同，对代码掌握的水平不同，所以会导致在项目的开发过程中代码质量良莠不齐。 对项目进行规范的过程主要是对代码的限制的过程，如果提高代码的准入门槛，进而可以将不规范以及不细心导致的错误降到最低。

## Typescript

由于 JavaScript 是一门弱类型的语言，因而由于类型上缺乏限制导致的问题也会很多，遇到比较多的错误如下：

*   对象中的值定义不明确

    由于未明确规定对象的值是否存在，会遇到很多例如`Can not read property xx of undefined!`，例如下例：

    ```javascript
    const a = {
      pro1: { name: "n1" },
      pro3: { name: "n3" },
    };
    ```

    `pro2`的属性可能是是没有的，如果按照`pro1`/`pro3`的方法去直接调用 name 属性`a.proj2.name`，这时就会报出从可能的 undefined 中读取属性。如果使用 ts，会在调用`a.proj2.name`时提示出错，因而避免发生这种错误。
* 参数传递不正确 例如我们写了一些类或者方法，如果缺乏类型校验，只能调用者来自行保证，这种约束是非常弱的。如果之后函数或者类的参数类型发生了变动，需要逐一去排查原参数调用是否出现问题。如果使用 typescript，就可以通过它的检查来节省人工的做法。

虽然 typescript 的使用会更加规范我们的代码，但是如果从一个已经维护了许久的 JavaScript 项目全量迁移到 ts 是非常痛苦的。 如果是一个新项目，建议从一开始就使用 ts 来维护，这样可以从开始就定义好需要的类型，对项目做一些类型的抽象也会相对简单。如果是一个维护了一段时间的 JavaScript 项目，建议使用逐渐替换的方法来实现 ts 化。 对于维护了一段时间的 JavaScript 项目，一个行得通的方案是通过 lint-staged 来逐渐替换 js 文件。这里有两个原则：

* 新开发的文件要以 ts 编写
* 新修改的 js 文件要迁移到 ts 文件

由于 ts 文件与 js 文件可以共存，在 ts 中，如果使用的组件是 ts 编写的，那么就能够使用 ts 的能力来进行检查；如果组件是 js 编写的，那么 ts 就不会进行检查。使用上述的两个原则能够实现逐步的 ts 化，不会引入新的 js 代码。在修改的过程中也会将 js 文件修改为 ts，最终将所有必要的文件都改为 ts。 虽然的 ts 的语法比较简单，但是如果编写扩展性高并且规范的类型还是需要对 ts 有一定的了解的，所以建议 ts 改造的过程需要有一个了解 ts 的人来把握类型书写的质量。

## Prettier

代码风格是所有程序员都要遇到的问题，不管是团队协作还是个人练习。往往很多公司想到提高代码质量和开发效率，首先就想到从代码风格入手。但现实中却很少看到代码风格管理很好的团队。因为在大多数时候，代码风格大多存在于讨论，无法确定一个让所有人都满意的方案，就很难执行下去。

由此 Prettier 提供了一个方案，通过在配置文件中规定各种代码的风格，例如缩进等配置。通过这个配置文件来规范整个项目的代码风格。

## ESlint

ESLint 是一个开源的 JavaScript 的 linting 工具，使用 espree 将 JavaScript 代码解析成抽象语法树 (AST)，然后通过 AST 来分析我们代码，从而给予我们两种提示：

* 代码质量问题：使用方式有可能有问题
* 代码风格问题：风格不符合一定规则

如果没有特殊的规范要求，建议使用 eslint-config-airbnb 规范集。

与 ts 类似，从一个现有项目迁移到使用 Eslint 是比较困难的，推荐使用 lint staged 方案，逐渐迁移项目。保持新添加的以及新修改的文件都符合 Eslint 规范。

如果项目中使用了 ts，应该使用 typescript-eslint，而不是使用已被废弃的 tslint。

## StyleLint

与 Eslint 类似，进行样式文件（css/less/sass 等）的强制性规范。

## Lint Commit

在提交 git 的信息时，也是需要规范化提交的信息（git commit message）。规范化提交信息有几个好处，一是可以快速定位历史的提交，找到自己寻找的提交点；再者可以自动化生成 changelog。

规范化提交信息可以使用 commitizen，配合 cz-conventional-changelog，使用命令行提示选择并补充必要信息来进行 git 的提交。

## Lint Staged

lint staged 是指只针对 git 的暂存区的文件进行检查。使用 lint staged 就能做到只检查修改的文件，无需全量扫描，对于迁移的项目也可做到逐步进行检查。

lint staged 可以对不同的文件来制定不同的检查命令，一个配置文件如下：

```javascript
const fs = require("fs");
const TMP = ".tsconfig-lint.json";

module.exports = {
  "{src,server}/**/*.{ts,tsx}": (filenames) => {
    const json = JSON.stringify(
      {
        extends: "./tsconfig.json",
        include: [...filenames, "./src/externals.d.ts"],
      },
      null,
      2
    );
    fs.writeFileSync(TMP, json);
    return `tsc -p ${TMP} --noEmit --resolveJsonModule`;
  },
  "**/*.{js,ts,tsx,json,jsx,less}": "npm run lint:prettier",
  "{src,server}/**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
  "src/**/*.less": "npm run lint:style",
};
```

lint staged 往往搭配 husky 一起使用。husky 可以在 git 的钩子上定义不同的命令来进行检查。例如可以在 git 的 pre-commit 时期制定 lint-staged 的检查，这样如果检查不通过，就不会提交代码，保证了代码的规范。

## CI/CD

上述中提到的保证项目规范的点都是按照本地的检查来进行的，实际上，本地是无法保证提交到远程的信息是一定符合规范的。考虑这样一个过程：远程项目本身是符合规范的，但是本地写了一些不符合规范的代码，而且忽略掉了代码检查的阶段，这样提交到远程的项目是不符合规范的。所以，远程的代码也需要一定的手段来检查。 在代码托管的平台（github/gitlab 等），会有 runner 来运行一些命令，开发者可以在 ci 文件中设置这些命令。当代码推送到远端时，可以运行代码规范检查的命令，如果命令不通过，则不允许该代码合入远端，由此保证远端代码的规范。
