关于前端:超全面的前端工程化配置指南

27次阅读

共计 12149 个字符,预计需要花费 31 分钟才能阅读完成。

前端工程化配置指南本文解说如何构建一个工程化的前端库,并联合 Github Actions,主动公布到 Github 和 NPM 的整个具体流程。示例咱们常常看到像 Vue、React 这些风行的开源我的项目有很多配置文件,他们是干什么用的?他们的 Commit、Release 记录都那么标准,是否基于某种约定?废话少说,先上图!

上图标红就是相干的工程化配置,有 Linter、Tests,Github Actions 等,笼罩开发、测试、公布的整个流程。相干配置清单 EslintPrettierCommitlintHuskyJestGitHub ActionsSemantic Release 上面咱们从创立一个 TypeScript 我的项目开始,一步一步实现所有的工程化配置,并阐明每个配置含意以及容易踩的坑。初始化为了防止兼容性问题,倡议先将 node 降级到最新的长期反对版本。首先在 Github 上创立一个 repo, 拉下来之后通过 npm init - y 初始化。而后创立 src 文件夹,写入 index.ts。package.json 生成之后,我须要增加如下配置项:   “main”: “index.js”,+  “type”: “module”,   “scripts”: {“test”: “echo \”Error: no test specified\” && exit 1″},+  “publishConfig”: {+    “access”: “public”+}咱们将我的项目定义为 ESM 标准, 前端社区正逐步向 ESM 规范迁徙,从 Node v12.0.0 开始,只有设置了 “type”: “module”, Node 会将整个我的项目视为 ESM 标准,咱们就能够间接写裸写 import/export。publishConfig.access 示意以后我的项目公布到 NPM 的拜访级别,它有 restricted 和 public 两个选项,restricted 示意咱们公布到 NPM 上的是公有包(免费),拜访级别默认为 restricted, 因为咱们是开源我的项目所以标记为 public。配置创立我的项目之后,咱们开始装置工程化相干的依赖,因为咱们是 TypeScript 我的项目,所以也须要装置 TypeScript 的依赖。Typescript 先装置 TypeScript,而后应用 tsc 命名生成 tsconfig.json。npm i typescript -Dnpx tsc –init 而后咱们须要增加批改 tsconfig.json 的配置项,如下:{“compilerOptions”: {    / Basic Options /    “baseUrl”: “.”, // 模块解析根门路,默认为 tsconfig.json 位于的目录    “rootDir”: “src”, // 编译解析根门路,默认为 tsconfig.json 位于的目录    “target”: “ESNEXT”, // 指定输入 ECMAScript 版本,默认为 es5    “module”: “ESNext”, // 指定输出模块标准,默认为 Commonjs    “lib”: [“ESNext”, “DOM”], // 编译须要蕴含的 API,默认为 target 的默认值    “outDir”: “dist”, // 编译输入文件夹门路,默认为源文件同级目录    “sourceMap”: true, // 启用 sourceMap,默认为 false    “declaration”: true, // 生成 .d.ts 类型文件,默认为 false    “declarationDir”: “dist/types”, // .d.ts 类型文件的输入目录,默认为 outDir 目录    / Strict Type-Checking Options /    “strict”: true, // 启用所有严格的类型查看选项,默认为 true    “esModuleInterop”: true, // 通过为导入内容创立命名空间,实现 CommonJS 和 ES 模块之间的互操作性,默认为 true    “skipLibCheck”: true, // 跳过导入第三方 lib 申明文件的类型查看,默认为 true    “forceConsistentCasingInFileNames”: true, // 强制在文件名中应用统一的大小写,默认为 true    “moduleResolution”: “Node”, // 指定应用哪种模块解析策略,默认为 Classic  },  “include”: [“src”] // 指定须要编译文件,默认当前目录下除了 exclude 之外的所有.ts, .d.ts,.tsx 文件} 更多具体配置参考:www.typescriptlang.org/tsconfig 留神的点,如果你的我的项目波及到 WebWorker API,须要增加到 lib 字段中 ”lib”: [“ESN

ext”, “DOM”, “WebWorker”],
而后咱们将编译后的文件门路增加到 package.json,并在 scripts 中增加编译命令。

  • “main”: “index.js”,
  • “main”: “dist/index.js”,
  • “types”: “dist/types/index.d.ts”
    “type”: “module”,
  • “scripts”: {
  • “test”: “echo \”Error: no test specified\” && exit 1″
  • },
  • “scripts”: {
  • “dev”: “tsc –watch”,
  • “build”: “npm run clean && tsc”,
  • “clean”: “rm -rf dist”
  • },
    “publishConfig”: {
    “access”: “public”
    }
    types 配置项是指定编译生成的类型文件,如果 compilerOptions.declarationDir 指定的是 dist,也就是源码和 .d.ts 同级,那么 types 能够省略。

验证配置是否失效,在 index.ts 写入

const calc = (a: number, b: number) => {
return a – b
}
console.log(calc(1024, 28))
在控制台中执行

npm run build && node dist/index.js
会在 dist 目录中生成 types/index.d.ts、index.js、index.js.map,并打印 996。

Eslint & Prettier
代码标准离不开各种 Linter, 之所以把这两个放在一起讲,借用 Prettier 官网的一句话:“应用 Prettier 解决代码格局问题,应用 linters 解决代码品质问题”。尽管 eslint 也有格式化性能,然而 prettier 的格式化性能更弱小。

大部分同学编辑器都装了 prettier-vscode 和 eslint-vscode 这两个插件,如果你我的项目只有其中一个的配置,因为这两者局部格式化的性能有差别,那么就会造成一个的问题,代码别离被两个插件别离格式化一次,网上解决 prettier+eslint 抵触的计划形形色色,甚至还有把整个 rules 列表贴出来的。

那这里咱们依照官网举荐,用起码的配置去解决 prettier 和 eslint 的集成问题。

Eslint
首先装置 eslint,而后利用 eslint 的命令行工具生成根本配置。

npm i eslint -D
npx eslint –init
执行下面命令后会提醒一些选项,咱们顺次抉择合乎咱们我的项目的配置。

留神,这里 eslint 举荐了三种社区支流的标准,Airbnb、Standard、Google,因个人爱好我抉择了不写分号的 Standard 标准。

生成的.eslintrc.cjs 文件应该长这样

module.exports = {
env: {

browser: true,
es2021: true,
node: true

},
extends: [

'standard'

],
parser: ‘@typescript-eslint/parser’,
parserOptions: {

ecmaVersion: 12,
sourceType: 'module'

},
plugins: [

'@typescript-eslint'

],
rules: {
}
}
有些同学可能就要问了,这里为什么生成的配置文件名称是.eslintrc.cjs 而不是.eslintrc.js?

因为咱们将我的项目定义为 ESM,eslit –init 会自动识别 type,并生成兼容的配置文件名称,如果咱们改回.js 结尾,再运行 eslint 将会报错。呈现这个问题是 eslint 外部应用了 require()语法读取配置。

同样,这个问题也实用于其余性能的配置,比方前面会讲到的 Prettier、Commitlint 等,配置文件都不能以 xx.js 结尾,而要改为以后库反对的其余配置文件格式,如:.xxrc、.xxrc.json、.xxrc.yml。

验证配置是否失效,批改 index.ts

const calc = (a: number, b: number) => {

return a - b

}

  • console.log(calc(1024, 28))
  • // console.log(calc(1024, 28))
    在 package.json 中增加 lint 命令

    “scripts”: {
    “dev”: “tsc –watch”,
    “build”: “npm run clean && tsc”,

  • “lint”: “eslint src –ext .js,.ts –cache –fix”,
    “clean”: “rm -rf dist”
    },
    而后在控制台执行 lint,eslint 将会提醒 1 条错误信息,阐明校验失效。

npm run lint

1:7 error ‘calc’ is assigned a value but never used no-unused-vars

因为是 Typescript 我的项目所以咱们还要增加 Standard 标准提供的 TypeScrip 扩大配置(其余标准同理)

装置 eslint-config-standard-with-typescript

npm i eslint-config-standard-with-typescript -D
增加批改 .eslintrc.cjs

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  • extends: [‘standard’]
  • extends: [‘standard’, ‘eslint-config-standard-with-typescript’],
    parser: ‘@typescript-eslint/parser’,
    parserOptions: {
    ecmaVersion: 12,
    sourceType: ‘module’,
  • project: ‘./tsconfig.json’
    },
    plugins: [‘@typescript-eslint’],
    rules: {}

    }
    

验证配置是否失效

在控制台执行 lint,eslint 将会提醒 2 条错误信息,阐明校验失效。

npm run lint

1:7 error ‘calc’ is assigned a value but never used no-unused-vars

1:14 error Missing return type on function

Prettier
当初咱们依照官网的举荐形式,把 prettier 集成到 eslint 的校验中。

装置 prettier 并初始化配置文件

npm i prettier -D
echo {}> .prettierrc.json
而后在.prettierrc.json 增加配置,这里只须要增加和你所选标准抵触的局部。

{
“semi”: false, // 是否应用分号
“singleQuote”: true, // 应用单引号代替双引号
“trailingComma”: “none” // 多行时尽可能应用逗号结尾
}
更多配置详见:prettier.io/docs/en/opt…

装置解决抵触须要用到的两个依赖

eslint-config-prettier 敞开可能与 prettier 抵触的规定
eslint-plugin-prettier 应用 prettier 代替 eslint 格式化
npm i eslint-config-prettier eslint-plugin-prettier -D
再增加批改 .eslintrc.cjs,如下:

module.exports = {
env: {

browser: true,
es2021: true,
node: true,

},

  • extends: [‘standard’, ‘eslint-config-standard-with-typescript’],
  • extends: [‘standard’, ‘eslint-config-standard-with-typescript’, ‘prettier’],
    parser: ‘@typescript-eslint/parser’,
    parserOptions: {
    ecmaVersion: 12,
    sourceType: ‘module’,
    project: ‘./tsconfig.json’,
    },
  • plugins: [‘@typescript-eslint’],
  • plugins: [‘@typescript-eslint’, ‘prettier’],
  • rules: {},
  • rules: {
  • ‘prettier/prettier’: ‘error’
  • },
    }
    而后验证配置是否失效,批改 index.ts
  • const calc = (a: number, b: number) => {
  • const calc = (a: number, b: number): number => {
    return a – b
    }
  • // console.log(calc(1024, 28))
  • console.log(calc(1024, 28))
    而后在控制台执行 lint, 这里 prettier 和 eslint 的行为已保持一致,如果没有报错,那就胜利了。

npm run lint
咱们当初曾经实现了 eslint 和 prettier 的集成配置。和编辑器无关,也就是说无论你应用什么编辑器,有没有装置相干插件,都不会影响代码校验的成果。

Husky
因为一个我的项目通常是团队单干,咱们不能保障每个人在提交代码之前执行一遍 lint 校验,所以须要 git hooks 来自动化校验的过程,否则禁止提交。

装置 Husky 并生成.husky 文件夹

npm i husky -D
npx husky install
而后咱们须要在每次执行 npm install 时主动启用 husky

如果你的 npm 版本大于等于 7.1.0

npm set-script prepare “husky install”
否则手动在 package.json 中增加

“scripts”: {

"dev": "tsc --watch",
"build": "npm run clean && tsc",
"lint": "eslint src --ext .js,.ts --cache --fix",
"clean": "rm -rf dist",
  • “prepare”: “husky install”
    },
    而后增加一个 lint 钩子

npx husky add .husky/pre-commit “npm run lint”
相当于手动在.husky/pre-commit 文件写入以下内容:

!/bin/sh

. “$(dirname “$0″)/_/husky.sh”
npm run lint
测试钩子是否失效,批改 index.ts

const calc = (a: number, b: number): number => {

return a - b

}

  • console.log(calc(1024, 28))
  • // console.log(calc(1024, 28))
    而后提交一条 commit, 如果配置正确将会主动执行 lint 并提醒 1 条错误信息,commit 提交将会失败。

git add .
git commit -m ‘test husky’

1:7 error ‘calc’ is assigned a value but never used

Commitlint
为什么须要 Commitlint,除了在后续的生成 changelog 文件和语义发版中须要提取 commit 中的信息,也利于其他同学剖析你提交的代码,所以咱们要约定 commit 的标准。

装置 Commitlint

@commitlint/cli Commitlint 命令行工具
@commitlint/config-conventional 基于 Angular 的约定标准
npm i @commitlint/config-conventional @commitlint/cli -D
最初将 Commitlint 增加到钩子

npx husky add .husky/commit-msg ‘npx –no-install commitlint –edit “$1″‘
创立.commitlintrc,并写入配置

{
“extends”: [

"@commitlint/config-conventional"

]
}
留神,这里配置文件名应用的是.commitlintrc 而不是默认的.commitlintrc.js,详见 Eslint 章节

测试钩子是否失效,批改 index.ts,让代码正确

const calc = (a: number, b: number): void => {

console.log(a - b)

}

  • // calc(1024, 28)
  • calc(1024, 28)
    提交一条不符合规范的 commit,提交将会失败

git add .
git commit -m ‘add eslint and commitlint’
批改为正确的 commit,提交胜利!

git commit -m ‘ci: add eslint and commitlint’
Angular 标准阐明:

feat:新性能
fix:修补 BUG
docs:批改文档,比方 README, CHANGELOG, CONTRIBUTE 等等
style:不扭转代码逻辑 (仅仅批改了空格、格局缩进、逗号等等)
refactor:重构(既不修复谬误也不增加性能)
perf:优化相干,比方晋升性能、体验
test:减少测试,包含单元测试、集成测试等
build:构建零碎或内部依赖项的更改
ci:自动化流程配置或脚本批改
chore:非 src 和 test 的批改,公布版本等
revert:复原先前的提交
Jest
美好生活从测试覆盖率 100% 开始。

装置 jest,和类型申明 @types/jest,它执行须要 ts-node 和 ts-jest

这里临时固定了 ts-node 的版本为 v9.1.1,新版的 ts-node@v10.0.0 会导致 jest 报错,期待官网修复,详见:issues

npm i jest @types/jest ts-node@9.1.1 ts-jest -D
初始化配置文件

npx jest –init
而后批改 jest.config.ts 文件

// A preset that is used as a base for Jest’s configuration

  • // preset: undefined,
  • preset: ‘ts-jest’
    将测试命令增加到 package.json 中。

    “scripts”: {
    “dev”: “tsc –watch”,
    “build”: “npm run clean && tsc”,
    “lint”: “eslint src –ext .js,.ts –cache –fix”,
    “clean”: “rm -rf dist”,
    “prepare”: “husky install”,

  • “test”: “jest”
    },
    创立测试文件夹__tests__和测试文件__tests__/calc.spec.ts

批改 index.ts

const calc = (a: number, b: number): number => {

return a - b

}

  • // console.log(calc(1024, 28))
  • export default calc
    而后在 calc.spec.ts 中写入测试代码

import calc from ‘../src’

test(‘The calculation result should be 996.’, () => {
expect(calc(1024, 28)).toBe(996)
})
验证配置是否失效

在控制台执行 test,将会看到测试覆盖率 100% 的后果。

npm run test
最初咱们给__tests__目录也加上 lint 校验

批改 package.json

“scripts”: {

"dev": "tsc --watch",
"build": "npm run clean && tsc",
  • “lint”: “eslint src –ext .js,.ts –cache –fix”,
  • “lint”: “eslint src tests –ext .js,.ts –cache –fix”,
    “clean”: “rm -rf dist”,
    “prepare”: “husky install”,
    “test”: “jest”
    },
    这里如果咱们间接执行 npm run lint 将会报错,提醒__tests__文件夹没有蕴含在 tsconfig.json 的 include 中,当咱们增加到 include 之后,输入的 dist 中就会蕴含测试相干的文件,这并不是咱们想要的成果。

咱们应用 typescript-eslint 官网给出的解决方案,如下操作:

新建一个 tsconfig.eslint.json 文件,写入以下内容:

{
“extends”: “./tsconfig.json”,
“include”: [“/.ts”, “/.js”]
}
在.eslintrc.cjs 中批改

parserOptions: {

ecmaVersion: 12,
sourceType: 'module',
  • project: ‘./tsconfig.json’
  • project: ‘./tsconfig.eslint.json’
    },
    而后验证配置是否失效,间接提交咱们增加的测试文件, 能正确提交阐明配置胜利。

git add .
git commit -m ‘test: add unit test’
Github Actions
咱们通过 Github Actions 实现代码合并或推送到主分支,dependabot 机器人降级依赖等动作,会主动触发测试和公布版本等一系列流程。

在我的项目根目录创立.github/workflows 文件夹,而后在外面新建 ci.yml 文件和 cd.yml 文件

在 ci.yml 文件中写入:

name: CI

on:
push:

branches:
  - '**'

pull_request:

branches:
  - '**'

jobs:
linter:

runs-on: ubuntu-latest
steps:
  - uses: actions/checkout@v2
  - uses: actions/setup-node@v2
    with:
      node-version: 16
  - run: npm ci
  - run: npm run lint

tests:

needs: linter
runs-on: ubuntu-latest
steps:
  - uses: actions/checkout@v2
  - uses: actions/setup-node@v2
    with:
      node-version: 16
  - run: npm ci
  - run: npm run test

下面配置大略意思就是,监听所有分支的 push 和 pull_request 动作,主动执行 linter 和 tests 工作。

GithubActions 更多用法参考:github.com/features/ac…

而后推送代码,验证配置是否失效

git add .
git commit -m ‘ci: use github actions’
git push
此时关上以后我的项目的 Github 页面,而后点击顶部 Actions 菜单就会看到正在进行的两个工作,一个将会胜利(测试),一个将会失败(公布)。

下面只是实现了代码自动测试流程,上面实现主动公布的流程。

在此之前须要到 NPM 网站上注册一个账号(已有可疏忽),并创立一个 package。

而后创立 GH_TOKEN 和 NPM_TOKEN(留神,不要在代码中蕴含任何的 TOKEN 信息):

如何创立 GITHUB\_TOKEN(创立时勾选 repo 和 workflow 权限)
如何创立 NPM\_TOKEN(创立时选中 Automation 权限)
将创立好的两个 TOKEN 增加到我的项目的 Actions secrets 中:

Github 我的项目首页 -> 顶部 Settings 菜单 -> 侧边栏 Secrets

而后批改 package.json 中的“name”,“name”就是你在 NPM 上创立的 package 的名称。

在 cd.yml 文件中写入:

name: CD

on:
push:

branches:
  - main

pull_request:

branches:
  - main

jobs:
release:

runs-on: ubuntu-latest
steps:
  - uses: actions/checkout@v2
  - uses: actions/setup-node@v2
    with:
      node-version: 16
  # https://github.com/semantic-release/git/issues/209
  - run: npm ci --ignore-scripts
  - run: npm run build
  - run: npx semantic-release
    env:
      GH_TOKEN: ${{secrets.GH_TOKEN}}
      NPM_TOKEN: ${{secrets.NPM_TOKEN}}

因为“黑命贵”,Github 已将新我的项目的默认分支名称更改为“main”,详见:issues,为了不便,前面对立称为 主分支

所以如果你的主分支名称是“main”,下面的 branches 须要批改为:

on:
push:

branches:
  - main

pull_request:

branches:
  - main

而后装置语义发版依赖,须要用到 semantic-release 和它的插件:

semantic-release:语义发版外围库
@semantic-release/changelog:用于主动生成 changelog.md
@semantic-release/git:用于将公布时产生的更改提交回近程仓库
npm i semantic-release @semantic-release/changelog @semantic-release/git -D
在我的项目根目录新建配置文件.releaserc 并写入:

{
“branches”: [“master”],
“plugins”: [

"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/github",
"@semantic-release/npm",
"@semantic-release/git"

]
}
这里同样,如果你的主分支名称是“main”,下面的 branches 须要批改为:

“branches”: [“+([0-9])?(.{+([0-9]),x}).x”, “main”],
最初新建分支 develop 分支并提交工作内容。

git checkout -b develop
git add .
git commit -m ‘feat: complete the CI/CD workflow’
git push –set-upstream origin develop
git push
而后将 develop 分支合并到 主分支,并提交,留神:这个提交会触发测试并 公布版本 (主动创立 tag 和 changelog)

git checkout master
git merge develop
git push
实现下面操作之后,关上 Github 我的项目主页 和 NPM 我的项目主页 能够看到一个 Release 的更新记录。

最初切回到 develop 分支,创立一个自动更新依赖的 workflow。

在.github 文件夹中创立 dependabot.yml 文件,并写入内容:

version: 2
updates:
# Enable version updates for npm

  • package-ecosystem: ‘npm’

    Look for package.json and lock files in the root directory

    directory: ‘/’

    Check the npm registry for updates every day (weekdays)

    schedule:
    interval: ‘weekly’

提交并查看 workflows 是否全副通过,再合并到 主分支 并提交,这个提交不会触发版本公布。

git pull origin master
git add .
git commit -m ‘ci: add dependabot’
git push

git checkout master
git merge develop
git push
触发版本公布须要两个条件:

只有当 push 和 pull_request 到 主分支 上才会触发版本公布
只有 commit 前缀为 feat、fix、perf 才会公布,否则跳过。
更多公布规定,详见:github.com/semantic-re…

SemanticRelease 应用形式,详见:semantic-release.gitbook.io

如果你能正确配置下面所有步骤,并胜利公布,那么祝贺你!你领有了一个齐全自动化的我的项目,它领有:主动依赖更新、测试、公布,和主动生成版本信息等性能。

残缺的我的项目示例:@resreq/event-hub

结语
本文未波及到:组件库、Monorepo、Jenkins CI 等配置,但能笼罩绝大部前端我的项目 CI/CD 流程。

有些中央讲得比拟细,甚至有些啰嗦,但还是心愿能帮忙到大家!撒花!🎉🎉🎉

正文完
 0