版权声明:本人文章仅在掘金平台发布,请勿抄袭搬运,转载请注明作者及原文链接。
网页版带有主题和代码高亮,阅读体验更佳~
刚学习 Vue
、React
的时候,还只会使用 cli
创建项目。Vue
内置的 ESlint
工具最令人头疼,常常报错导致项目跑不起来,最后只能删除项目重新创建。上一家公司也是,都是水平很差的前端,项目根本不敢开 ESlint
,没有人能解决 ESlint
的报错问题,导致项目代码里充斥着各种奇珍异兽。这些东西如果不熟悉,不理解,遇到问题很难去解决,而此类工程化相关的东西,我觉得是 3
到 5
年前端能和别人最快拉开差距的知识点。抛开 JS
、框架,我们每天打交道最多的就是这些东西,一个优秀的前端项目工程,代码提交检查、代码格式规范是基本配置,在此基础上才能去谈 TS
、CI CD
、单元测试等内容。
本篇文章就围绕这些工程化工具为大家做一次入门引领,从 0
到 1
搭建一个开源项目,从整体的视角来看待这些工具,从而更好地掌握工具,而不是排斥工具、害怕工具。
阅读本篇文章,你将学到以下工具的使用:
工具 | 描述 |
---|---|
ESlint | 代码格式校验 |
husky | 执行 git 钩子函数,一般用来检查 commit 信息 |
lint-staged | 与 husky 配套使用,只检查暂存区的代码 |
conventional-changelog-cli | 生成 CHANGELOG.md 文件,记录库的版本日志 |
LICENSE | 如何选择开源协议及如何快速生成开源协议 |
.editorconfig | 编辑器配置 |
bumpp | 自动升级版本而不用每次都手动改 |
package.json | 依赖配置文件,一些你在平时项目不会用也不会关注的字段 |
vite | 如何构建一个 npm 包 |
从开源项目中学习
随便打开一个开源项目的 github 仓库,就会发现除了核心代码之外,还有很多工程化相关的配置文件:
.husky
.eslintrc
.eslintignore
.editorconfig
.npmrc
.stylelint.js
.commitlint.config.js
README.md
LICENSE
CHANGELOG.md
除此之外,还有一个最关键的文件:package.json
,这是最关键的一员,我们会在具体的知识点中来讲解这个文件。
ant-design 的部分工程配置文件
基于 vite 搭建项目
出于方便高效等各种原因选择 Vite
,但是这不是最关键的内容,你也可以使用 Webpack
。为了保持学习的一致性,建议你和我保持一样的操作。
使用 Vite
,对 Node
版本有一定要求,你可以选择和我保持同样的版本:
Vite
需要 Node.js 版本 14.18+,16+。然而,有些模板需要依赖更高的Node
版本才能正常运行,当你的包管理器发出警告时,请注意升级你的Node
版本。
包管理工具我选择 yarn
,你也可以选择 pnpm
。
在终端执行命令创建项目:yarn create vite
,我们这里并不依赖任何框架,上下箭头移动选择 others
,然后回车:
它又会问我们是不是 Electron
项目,当然也不是,选择第一个回车:
接下来它会询问我们选择一个预设,这里我们选择库模式:
它还会问我们是否需要 Typescript
,我们这里需要:
Vite
为我们生成的项目模板:
可以看到它自带了一个 lib
文件,这其实就是我们最终生成的库的源码包,但是现在还只是个初级版,还不符合我们的需求。接下来我们需要做一些额外的配置。
编写 vite.config.js
首先明确我们需要支持的模块化,如果需要同时支持 esm
和 cjs
,那么我们可以生成两个最终产物,分别定义两种模块化的入口,如果你只支持 esm
或者 cjs
那么只生成一个最终产物就可以了,当然你还可以使用 UMD
,一个最终产物同时支持以上两种模块化。这里我们以同时生成 esm
和 cjs
两种包为例编写配置文件。
import { defineConfig } from 'vite';
export default defineConfig({
build: {
target: 'modules', // 详情参考官方文档:https://cn.vitejs.dev/config/build-options.html#build-target
minify: true, // 是否开启代码压缩
rollupOptions: {
input: ['src/index.ts'], // 打包的入口文件 https://cn.rollupjs.org/configuration-options/#input
output: [ // 产物输出配置
{
format: 'es', // 指定模块化类型 https://cn.rollupjs.org/configuration-options/#output-format
entryFileNames: '[name].js', // 入口文件名,默认 https://cn.rollupjs.org/configuration-options/#output-entryfilenames
preserveModules: true, // 该选项将使用原始模块名作为文件名
dir: 'es', // 输出的目录
preserveModulesRoot: 'src' // 确保输出的目录和输入时的一致
},
{
format: 'cjs', // 指定模块化类型
entryFileNames: '[name].js',
preserveModules: true,
dir: 'lib',
preserveModulesRoot: 'src'
}
]
},
lib: { // https://cn.vitejs.dev/config/build-options.html#build-lib
entry: './index.ts', // 定义作为库的入口是哪个
}
},
});
我们执行下 yarn build
来看看当前配置会为我们生成什么样的产物:
来看个小插曲,因为我们 Vite
生成的模板 src
下的入口默认是 main.ts
,但是我们在配置文件写的是 index.ts
,它找不到 index.ts
文件就报错了:
首先需要将 main.ts
改名为 index.ts
,然后需要改写里面的代码,我们随意写个变量并导出,因为我们是库,如果不导出模块是会报错的。
来看看最终打包的产物:一个 es
一个 lib
,这里有了这两个文件后,我们接下来可以做的事就多了。
针对 es 和 lib 做处理
第一件事,这两个包我不希望他们被提交到仓库里,我需要在 .gitignore
文件里忽略它们:
...
es
lib
...
第二件事,这两个包必须得在 package.json
里定义它的入口文件,否则发布成 npm
库后,别人下载下来找不到入口文件,也就没法引用代码了。
{
"name": "vite-project",
"private": false,
"version": "0.0.0",
"type": "module",
"main": "lib/index.js",
"module": "es/index.js",
"types": "./index.d.ts",
"scripts": {
"dev": "vite",
"build": "tsc && vite build"
},
"files": [
"es",
"lib",
"index.d.ts"
],
"devDependencies": {
"typescript": "^4.9.4",
"vite": "^4.0.4"
}
}
main
字段我们定义 cjs
模块的入口,module
我们定义 es
模块的入口。另外,private
设置为 false
,files
是指我们最终发布 npm
时要上传的文件,现在我们需要将 es
与 lib
两个文件加进来,这样我们发布代码时才能将代码发布上去,不然别人下载你的包会连代码都没有,这里一定小心,非常重要!
配置 ESlint
第一步,在终端执行命令:npx eslint --init
,它会让我们选择模式:
- 仅仅检查语法
- 检查语法,发现问题
- 检查语法,发现问题,强制代码风格
这里我们选择第二个。
接下来它会询问我们使用的模块化,我们选择 esm
:
它会问我们的项目是否基于某一个框架,由于我们这里没有,就不选,你可以根据你自己的情况选择:
接下来的就不是很重要了,我的选择给大家一个参考:
最终生成一个 .eslintrc.cjs
的规则配置文件,但是它会报红。因为我们是使用的 esm,我们把后缀 cjs
换成 js
就行了。不过这样其实也不太好,因为模块化可能会有各种潜在问题,所以我们直接将文件格式改成 json
,也是一样的。这里顺嘴提一句,当项目存在多个 .eslintrc
配置文件且后缀不同时,优先级是 js > json > yaml
,没有后缀的话默认是 json
格式。
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"overrides": [
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"semi": 2
}
}
我们可以在 rules
字段下新增一个规则 "semi": 2
,检查下 index.ts
中有没有报红,报红就说明配置生效了。其中,0
是关闭,1
是警告,2
是报错,还有其它的一些规则值,大家感兴趣可以自行研究,我们这里使用 012
已经够用了。
接下来简单讲讲配置中各个主配置字段的含义:
eslintrc 配置字段含义解读
字段 | 描述 |
---|---|
env | 在配置文件中使用 env 键指定环境,并通过将每个环境设置为 true 来启用想要的环境 |
globals | 要在配置文件中配置全局变量,请将 globals 配置属性设置成对象,其中包含为你要使用的每个全局变量命名的键。对于每个全局变量的键,将相应的值设置为 writable 以允许变量被覆盖,或者 readonly 以禁止覆盖。比如你可以把 window 禁用了,设置为只读 |
extends | eslint 检查用哪些规范,包括内置的和第三方的 |
overrides | 覆盖配置中基于文件 glob 模式的设置 (你只要知道基本用不到就对了) |
parser | 配置解析器 |
parserOptions | ESLint 允许你指定你想要支持的 JavaScript 语言选项。默认情况下,ESLint 希望使用 ECMAScript 5 语法。你可以通过使用解析器选项来覆盖这一设置,以实现对其他 ECMAScript 版本以及 JSX 的支持。 |
plugins | 对于特殊语法需要使用插件进行识别,例如对 TS 、React 语法的检查都依赖插件 |
rules | 具体的校验规则,比如有分号没分号,允不允许 console.log 等 |
到这里我们的 eslint
基本就配置完了,其实很简单对不对?但是我们看到 .eslintrc
的缩进是 4
个,我个人是不喜欢 4
缩进的,这里就引出了另一个配置文件:.editorconfig
。
不过在讲 .editorconfig
之前,还有一个文件和 ESlint
有关,那就是 .eslinignore
。并不是所有文件都需要代码检查,ESlint
一般也只辅助我们检查 js
、ts
代码,所以有些文件我们得忽略掉。在根目录下创建文件:.eslintignore
:
一些构建后的产物没有检查的意义,所以有必要忽略掉。目前我们需要忽略的文件就是 node_modules
、test
、dist
、build
、es
、lib
。你若有其它的可忽略文件加进来即可。
.editorconfig
这个文件有什么意义?
.editorconfig
有助于为跨不同编辑器和ide
从事同一项目的多个开发人员维护一致的编码风格。.editorconfig
项目由用于定义编码样式的文件格式和一组文本编辑器插件组成,这些插件使编辑器能够读取文件格式并遵循已定义的样式。.editorconfig
文件很容易阅读,并且可以很好地与版本控制系统配合使用。
假如你用 macOS
开发,我用 windows
开发,那么两个系统使用的编码格式不同,就会出现乱码,你一定遇到过下载的代码在换行时有一个 DR
符号的情况,这就是系统差异导致的。还有就是,有的人喜欢 4
个缩进,有的喜欢 2
个,缩进不同就会导致代码差异比较大,有些人还比较喜欢使用代码格式化工具,一保存就会自动格式化,导致代码一堆冲突,这时候解决起来就麻烦了。
.editorconfig
长什么样呢?一般是在项目的根目录下新建一个 .editorconfig
,不带任何后缀即可,它会被编辑器自动解析。
贴一下我的配置:
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line-length = off
trim_trailing_whitespace = false
我这里将代码缩进设置为 2
个,刚才的 .eslintrc
立马有了反应,缩进线变了,可以进行 2
个空格的缩进了:
这就是 .editorconfig
的能力,可以控制编辑器的缩进、utf
编码、去除空白符等等。这对团队协作非常重要,有利于大家的项目保持统一的风格,避免一些非代码问题导致的问题降低开发效率。
husky & lint-staged 代码检查并修复
团队协作最重要的就是标准和规范,没有标准规范就会有各种各样的问题,例如代码格式混乱、commit
信息千奇百怪。标准规范制定虽然容易,执行落地却很困难,所以必须借助于工具。ESlint
虽然已经帮助我们避免大量的代码格式问题了,但是人总是会出错的,为了错误代码不被提交,特别是有些人使用未定义的变量,这样一定会导致代码报错,从而导致生产故障,这时就需要其它工具来进行辅助检查。
husky
加上 lint-staged
就是这种帮助代码检查的工具,配合 ESlint
一起使用。
暂存区代码 commit 前进行修复
第一步,执行 yarn add husky lint-staged -D
,先下载依赖。
第二步,执行 npx husky-init && husky install
。该命令会在 package.json
中新增一个 script
:"prepare": "husky install"
。
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"prepare": "husky install"
},
紧接着会下载 .husky
文件,也就是 git hooks
,这些钩子会在一定的时机执行。husky
的 hooks
有 pre-commit
、commit-msg
、pre-push
等。同时,它会生成一个 pre-commit
的 shell
脚本文件,负责执行 pre-commit
钩子。这个脚本文件就是在里面输入 shell
命令,husky
会在一定的 git
执行时机执行这个命令。你可以理解为类似于在终端执行的命令,只不过写在了这个文件里而已。
pre-commit
钩子是在 git commit
之前执行,我们现在要做的就是利用这个钩子在代码 commit
前修复一些错误。
第三步,package.json
新增 lint-staged
配置,这里的意思是在每次 commit
执行之前也就是 pre-commit
的时候执行 lint-staged
命令。它会匹配以 .js
、.ts
结尾的文件,并修复这些文件里的代码格式错误,并继续将修改后的文件存入暂存区,pre-commit
之后才会执行我们提交的 commit
命令。
"lint-staged": {
"src/**/*.{js,ts}": [
"eslint --fix",
"git add"
]
},
第四步,我们需要将 pre-commit
文件里的 npm test
替换为 npx lint-staged
,这样它就会在 pre-commit
钩子执行时调用第三步里的方法,对暂存区的代码进行扫描和修复。
我们来测试一下,src/index.ts
里我们之前留了几个分号的报错,现在我们将它进行 commit
:
可以看到分号的错误已经被修复。
对 commit 信息进行格式检查
如果你接触过一些正式的 commit
规范,会知道我们的 commit
信息一般是这样:feat: 搭建项目骨架
。首先是此次变更的关键词,常见的有 feat
、style
、docs
、chore
等。具体看下表:
关键字 | 具体含义 |
---|---|
feat | 新增特性 (feature) |
fix | 修复 bug(bug fix) |
docs | 修改文档 (document) |
style | 代码格式修改 |
refactor | 代码重构 (refactor) |
perf | 改善性能 (performance) |
test | 测试 |
build | 变更项目构建或外部依赖 |
chore | 杂项 |
revert | 代码回退 |
这样的信息我们也有工具进行规范。我相信有人看到这里会觉得这种东西似乎没有什么作用,有点多此一举了。仁者见仁智者见智吧,我觉得专业的前端团队这种东西应该都会做,毕竟工程化的东西,其它的都能做,这件小事也就是顺手的事情,有利无弊的事,做了总比没做好。
第一步,下载两个依赖:yarn add @commitlint/cli @commitlint/config-conventional -D
。
第二步,终端执行命令:npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
,它会在 .husky
下生成一个 commit-msg
的 shell
脚本文件,并且会在我们 commit
提交 message
时执行 npx --no-install commitlint --edit
命令,对我们的信息进行校验。
但是怎么个校验法它并不知道,所以需要我们在根目录下编写一个配置文件 commitlint.config.js
:
const types = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'release', 'chore', 'revert'];
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-empty': [2, 'never'],
'type-enum': [2, 'always', types],
'scope-case': [0, 'always'],
'subject-empty': [2, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [2, 'always', 88],
},
};
以上配置仅供参考。来测试下:
一个小插曲,报错的信息说不支持 esm
,需要导出 cjs
模块。那么我们需要将 commitlint.config.js
替换为 commitlint.config.cjs
,并将其进行 module.exports
。
继续测试,可以看到配置已经生效了。
不过有时候不想这个配置生效,觉得这个校验很麻烦怎么办?就比如前同事留下的垃圾代码,每次 commit
触发校验一堆报错,老代码又不想动,新代码又必须提交,怎么办?其实加个参数就可以了:--no-verify
。例如:git commit -m 'feat: 完成项目骨架' --no-verify
,它就会跳过 commit 校验,包括代码格式校验及 commit message
校验。
生成 CHANGELOG.md 更新日志
打开一些开源项目的 github 仓库,发现人家对每次版本更新都有很详细的记录,有时候觉得人家真细致,不愧是开源作者,但其实人家也是借助的工具。
第一步,执行命令:yarn add conventional-changelog-cli -D
。
第二步,在 package.json
中配置脚本:"changelog": "conventional-changelog -p -i CHANGELOG.md -s"
。
我们来测试下:yarn changelog
。
就这样两步就能生成 CHANGELOG.md
。
自动升级版本号
每次发布新的版本都要手动更改 package.json
的 version
实在是麻烦,而且最麻烦的是会忘记更改。可以下载 yarn add bumpp -D
,每次 release
之前都执行下 bumpp
,它会提示我们对版本进行升级,还有各种合适的版本号推荐,例如 beta
版、major
版等。
在 package.json
中新增一个 script
:"version": "bumpp"
。终端执行 yarn version
看看效果:
它会继续问我们是否需要更改到其它版本,我们这里不需要,直接选择 as-is 0.0.2
:
最后它会问我们是否需要继续,我们选择 N
:
主要的功能是防止忘记更改版本,可以将其放到每次发布之前执行。
快速生产开源协议
开源项目离不开开源协议,但是不可能手写开源协议吧?其实这个也不用我们自己操作,vscode
有插件帮助我们快捷生成。
快捷键 shift command (ctrl) p
可进行选择:
开源协议有很多,这里就不展开了,一般选择 MIT
就可以。下面列个常见开源协议的资料,有兴趣的话可以细看。
协议名称 | 协议描述 |
---|---|
BSD (Berkeley Software Distribution license) | BSD 允许使用者修改和重新发布代码,也允许基于 BSD 代码上开发商业软件的发布和销售。遵循 BSD 协议的代码完全可控,必要的时候可以修改或者二次开发 |
MIT (Massachusetts Institute of Technology) | MIT 是宽松的许可协议,作者只想保留版权,而无任何其它限制。只需在发布的源代码、二进制可执行文件相关文档中包含 MIT 许可协议声明,便可自由的使用、修改源代码、作为商业软件再发布、甚至使用开源机构名字做产品的市场推广 |
Apache Licence 2.0 | 详见:开源协议详解 |
GPL (General Public License) | 代码的开源免费使用和引用,修改及衍生代码的开源免费使用,但其不允许修改后和衍生的代码做为闭源的商业软件发布和销售(只要使用 GPL 协议的相关类库与代码,则该软件亦必须采用GPL 协议,必须开源与免费) |
LGPL (Lesser General Public License) | LGPL 允许商业软件通过类库引用方式使用 LGPL 类库而不需要开源商业软件的代码,如果修改了 LGPL 协议的代码或衍生,则所有修改的代码和衍生的代码都必须采用 LGPL 协议 |
Mozilla (Mozilla Public License) | 在自己已有的源代码库上加一个接口,除了对接 Mozilla Public License 开源库的接口程序源代码以MPL许可的形式对外许可外,源代码中的其他源码可以不用 MPL 许可证的方式强制对外许可 |
完善 package.json 配置
配置完开源协议文件后,还需要在 package.json
里新增字段:"license": "MIT"
。
除此之外,还可以设置关键词,简要标记下库的功能和技术点:
"keywords": [
"vite",
"commitlint",
"husky",
"CHANGELOG",
"LICENSE",
"ESlint"
],
files
字段再把 README
和 LICENSE
带上,因为使用了 TS
,index.d.ts
也是必须上传的。另外,上传哪个类型声明文件,是因为我们配置了字段:"types": "./index.d.ts",
。这里配置的是谁,你在 files
字段里上传的类型声明文件就是谁。
"files": [
"es",
"lib",
"README",
"LICENSE",
"index.d.ts"
],
还有 repository
和 publishConfig
,分别记录库的代码仓库及发布时 npm
的镜像地址。如果是开源的话就是 https://registry.npmjs.org/
,不是开源的话可以设置成自己或者公司的私有镜像地址。
"repository": {
"type": "git",
"url": "https://github.com"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/"
}
贴一下完整的 package.json
:
{
"name": "open-source-config-template",
"private": false,
"version": "0.0.1",
"type": "module",
"main": "lib/index.js",
"module": "es/index.js",
"types": "./index.d.ts",
"license": "MIT",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext '.js,.ts' --fix",
"prepare": "husky install",
"commit": "git-cz",
"version": "bumpp",
"changelog": "conventional-changelog -p -i CHANGELOG.md -s",
"before-release": "yarn changelog && yarn build && bumpp"
},
"lint-staged": {
"src/**/*.{js,ts}": [
"eslint --fix",
"git add"
]
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"files": [
"es",
"lib",
"README",
"LICENSE",
"index.d.ts"
],
"keywords": [
"vite",
"commitlint",
"husky",
"CHANGELOG",
"LICENSE",
"ESlint"
],
"devDependencies": {
"@commitlint/cli": "^17.6.5",
"@commitlint/config-conventional": "^17.6.5",
"@typescript-eslint/eslint-plugin": "^5.59.8",
"@typescript-eslint/parser": "^5.59.8",
"bumpp": "^9.1.0",
"commitizen": "^4.3.0",
"conventional-changelog-cli": "^2.2.2",
"eslint": "^8.42.0",
"husky": "^8.0.0",
"lint-staged": "^13.2.2",
"typescript": "^4.9.4",
"vite": "^4.0.4"
},
"repository": {
"type": "git",
"url": "https://github.com"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/"
}
}
写在最后
做到现在,我们来看下整个项目的文件目录:
基本上已经与一个开源项目的配置差不太多了,如果需要 stylelint
等其它配置,继续往里面添加即可。不过不是所有项目都会写 CSS
,更细化的配置就是根据项目需要再添加了。
如果本篇文章对你有帮助,请点赞收藏关注三连一波,可以通过我的 vscode
截图看到我的 vscode
背景从亮变黑,从早写到晚写了整整一天,感谢 ~。
下面是本期文章配对的代码仓库:open-source-config-template
上面的地址失效了,不知道怎么没的,可以参考我另外两个库,都是类似的:
往期推荐
分享我在前端学习与开发中用到的神仙网站和工具 40+
👍🏻 110+
❤
uniapp 踩坑记录(二) 130+
👍🏻 150+
❤
闲来无事,摸鱼时让 chatgpt 帮忙,写了一个 console 样式增强库并发布 npm 100+
👍🏻 110+
❤
uniapp 初体验踩坑记录 30+
👍🏻 60+
❤
两小时学会 JS 正则表达式,终身不忘 50+
👍🏻
【一年前端必知必会】如何写出简洁清晰的代码 50+
👍🏻
【一年前端必知必会】了解 Blob,ArrayBuffer,Base64 40+
👍🏻 90+
❤