对代码进行格式化,是程序员的日常。格式化很爽,但是配置相关的规则、安装对应插件却很烦。与其每次碰到问题再慢慢排查,不如一次性融会贯通。
一、代码风格是什么(Code Conventions)
我们可以按照修改后是否会影响程序运行结果来把风格分为两个部分。例如下面的代码
var a= 1;
if (a=='1') console.log(true )
1. Format 格式 -> format 格式化(首字母大小写以便区分)
格式,简单来说就是变量后面是否需要空格,语句后面是否需要分号等,这一类无论如何改动,都不会影响代码运行结果的部分。对格式进行改变叫格式化。
使用如下规则对代码进行 format:
- 语句之后必须接分号
- 括号头尾需要 1 个空格
- 运算符前后需要 1 个空格
var a = 1;
if ( a == '1' ) console.log( true );
2. Source 源码 -> (source action 源动作)
源码,与格式相对的,是被修改之后会切实影响代码运行结果的部分。对源码进行改变叫源动作
使用如下规则对代码进行 source action:
- 未发生改变的变量必须使用
const
定义 - 必须使用
===
,禁止使用==
const a = 1;
if ( a === '1' ) console.log( true );
格式加源码,构成了代码风格
定义来源:vscode 任意选中一段代码之后右键
二、风格工具
风格当然是有各种各样的,有的工具可以设定自己的风格,有的工具强制使用特定风格。为了便于理解,本文只介绍业界最主流的工具(也很少有人用其他的)
Prettier
Prettier 专注于 Format,基本支持了前端项目相关的所有文件类型,包括但不限于
- JavaScript
- JSX
- Vue
- TypeScript
- CSS、Less、SCSS
- HTML
- JSON
- Markdown
Prettier 最大的特点是独裁,在自身规定的几百上千条 Format 规则中,可配置的仅有 20 条。这也导致其他工具与 Prettier 并存时,都需要迁就 Prettier 的规则
ESLint
ESLint 是目前所有 Lint 工具中使用最广泛的(前端范围内)。
计算机科学中,lint 是一种工具程序的名称,它用来标记源代码中,某些可疑的、不具结构性(可能造成bug)的段落。它是一种静态程序分析工具,最早适用于C语言,在UNIX平台上开发出来。后来它成为通用术语,可用于描述在任何一种计算机程序语言中,用来标记源代码中有疑义段落的工具。
—— wiki
ESlint 可配置的范围,包括 Format 和 Source,但是仅限于可以被转换成 AST 的代码,对于 CSS、HTML 等都无能为力(这也是为什么需要同时使用 ESLint 和 Prettier 的原因)
stylelint
前端开发基本等同于 HTML、JavaScript 和 CSS 开发。通过 Prettier + ESLint,对于风格中的 Format 已经完全统一,JavaScript 中的 Source 也被统一。所以现在还剩下基本可以忽略的 HTML 和 CSS 中的 Source 部分
我们选择使用 stylelint 来处理 CSS 相关 Source
这样基本可以限定前端开发过程中的所有风格
HTML | JavaScript | CSS | |
---|---|---|---|
Format | prettier | prettier | prettier |
Source | 无 | ESlint | stylelint |
三、使用说明
通常项目中使用风格工具,有三种途径
- A - 编译器引入相关插件
- B - webpack 引入相关 loader、plugin
- C - 命令行直接执行
先模拟一下项目中开发需求的流程
- 拉取项目代码,安装依赖,初始化项目配置风格设置,命令行格式化所有代码(C)
使用 webpack 编译项目
- 窗口中及时提示风格问题并自动修复(A)
- 控制台及时提示风格问题,不修正情况下打包失败(B)
- 使用 git 提交,触发钩子,检查风格通过之后才允许提交(C)
任何流程没有控制住,都有可能造成设定以外的代码风格
无论是通过函数、命令行或者插件来使用,风格化的过程基本都可以抽象为如下公式。
Result = format(sourceAction(src, config)) = Tool(src, config)
// 最终风格 = 格式化(源动作(源码,配置)) = 工具(源码,配置)
源码为常量。只要保证工具版本统一,配置相同,则不同途径均也可以得出相同的结果。规则和工具的选择从来不是难点,难点在于保证工具、配置的统一
接下来介绍如何保证 A、B、C 三种途径的统一
A - 编译器引入相关插件
工具
ESlint: The extension uses the ESLint library installed in the opened workspace folder. If the folder doesn't provide one the extension looks for a global install version.
Prettier: This extension will use prettier from your project's local dependencies (recommended)...
stylelint: The extension uses the stylelint library installed in the opened workspace folder...
根据各个工具的官方插件文档说明,插件会优先使用项目中的对应库 node_modules
,如果不存在则使用全局库。
配置
ESLint
ESLint 插件目前可以指定配置文件,如果不指定会读取 package.json 同级的配置文件
eslint.options: options to configure how ESLint is started using the ESLint CLI Engine API. Defaults to an empty option bag. An example to point to a custom .eslintrc.json file is:
{
"eslint.options": { "configFile": "C:/mydirectory/.eslintrc.json" }
}
Prettier
Prettier 的配置可以来源于三处,按优先级排列
- Prettier configuration file
.editorconfig
- Visual Studio Code Settings (Ignored if any other configuration is present)
实际使用中,使用第一个 package.json 同级的配置文件即可
stylelint
与 Prettier 类似
B - webpack 引入相关 loader、plugin
const StyleLintPluginfrom = require('stylelint-webpack-plugin');
//...
{
test: /\.(j|t)sx?$/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'eslint-loader',
options: {
configFile: getWorkSpacePath('.eslintrc.js'),
}
},
],
}
//...
plugins: [
new StyleLintPluginfrom({
configFile: getWorkSpacePath('.stylelintrc.js'),
})
]
//...
工具
所有库都需要安装在项目的 node_modules
中, eslint-loader 同时依赖于 eslint 库(stylelint-webpack-plugin 类似)
配置
所有工具基本都可以在 option 中指定配置文件
C - 命令行直接执行
工具
首先需要在 node_modules 中安装有对应的库,然后在 package.json 中设置对应脚本
{
//...
"scripts": {
"eslint": "eslint",
"prettier": "prettier",
"stylelint": "stylelint",
},
//...
}
之后可以在命令行中使用
yarn eslint --fix
-c, --config path::String Use this configuration, overriding .eslintrc.* config options if present
yarn prettier --write
--config ./my/.prettierrc
yarn stylelint --fix
--config ./my/.stylelintrc
配置
所有工具在使用均可以指定配置文件
所以
- 工具统一等价于把相关依赖全部安装到项目
**node_modules**
中,保证不使用全局依赖 - 配置统一等价于使用统一的配置文件。由于部分插件不能指定路径,只会默认读取
node_modules
同级,所以建议所以配置文件都放在这一层
四、实战
首先引入 Prettier,我们所有的 format 都使用 Prettier 来完成。
在项目中接入 Prettier
yarn add prettier -D
1. 安装插件
安装官方推荐的编译器插件 Prettier - Code formatter
(此处只考虑 VSCode),设置工作区的 .vscode/setting.json
切记一定要关闭其他带有格式化功能的插件,否则会发生冲突。特别是使用 VUE 框架时,记得关闭 vetur 的格式化。同时记得检查一下全局范围的 setting.json
,注释掉所有 format 相关的配置
{
// 保存时自动 Format
"editor.formatOnSave": true,
// 关闭 vetur 格式化
"vetur.format.enable": false,
}
2. 推荐插件
为了告诉其他协同开发的同事,安装正确的 Prettier 插件,添加插件名在 .vscode/extensions.json
中,同时把其他容易干扰格式化的插件设置为不推荐。
这样其他人在首次安装项目的同时也会收到对应的提示了。
{
"recommendations": [
"esbenp.prettier-vscode",
],
"unwantedRecommendations": ["hookyqr.beautify"]
}
3. 配置文件
在 package.json
同级创建 Prettier 的配置文件
// prettier.config.js or .prettierrc.js
module.exports = {
trailingComma: "es5",
tabWidth: 4,
semi: false,
singleQuote: true,
};
git 提交本次修改(尤其是 .vscode 文件夹)。之后任何人开发这个项目,只要安装了正确的插件,写出来的代码风格肯定都是统一的了
4. 限制提交
假设有一个同事头铁,就是不正确安装插件和配置编译器。他提交代码的风格就无法得到统一,所以需要再增加一层保险,在 git commit 时候需要校验所有改动过的文件,对这些文件使用 prettier --write
。
git commit 时触发、校验所有改动过的文件。这两个功能分别是两个插件
- husky
它能运行Githooks诸如 Pre-commit、pre-receive 和 post-receive。
用以在各个阶段触发不同校验,一般配合各种 lint 使用
- lint-staged
当项目文件比较多的时候,如果每次改动都对所有文件进行校验。势必导致等待时间变长。 解决上面的痛点就需要使用 lint-staged。它只会校验你提交或者说你修改的部分内容。
yarn add husky lint-staged -D
在 package.json
中添加字段
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
// src 目录下任意文件有改动,都使用 prettier --write 格式化之后再提交
"src/**/*.*": [
"prettier --write",
"git add"
]
}
这样基本就能保证项目的万无一失了(一失在于开发者删除 husky、lint-stage 配置🤦♂️)
在项目中接入 ESLint、stylelint
Prettier 只提供了风格中 Format 的部分,对于 Source,还需要接入其他工具。
- ESLint:处理 JavaScript 相关(TypeScript、JSX ...)的 Source action
- stylelint:处理 CSS 相关 (Less、SCSS) 的 Source action
流程基本和接入 Prettier 一样
1. 安装、推荐插件
安装 ESLint
stylelint
,并修改 setting.json
yarn add eslint stylelint -D
注意 formatOnSave
、codeActionsOnSave
两者的区别。之前说过,由 Prettier 进行所有的 format,然后再针对不同类型的文件使用各种的 Soruce action
相关讨论:https://github.com/microsoft/vscode/issues/87096#issuecomment-567750791
{
// 保存时自动 Format
"editor.formatOnSave": true,
// 保存时自动 Source action
"editor.codeActionsOnSave": {
"source.fixAll": true, // 开启所有工具的 source action
//"source.fixAll.eslint": true, // 开启 ESLint 的 source action
//"source.fixAll.stylelint": true, // 开启 stylelint 的 source action
},
"vetur.format.enable": false,
}
修改 extensions.json
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint"
],
"unwantedRecommendations": ["hookyqr.beautify"]
}
2. 配置文件
在 package.json
同级创建 ESlint、stylelint 的配置文件,具体如何设置此处就不说明了。要注意的就是一定要给 Prettier 的设置让位
**
给 ESLint、stylelint 安装兼容 prettier 的插件
yarn add eslint-config-prettier stylelint-config-prettier -D
// .eslintrc.js
module.exports = {
//...
env: {
browser: true,
es6: true,
},
extends: ["eslint:recommended", "prettier"],
plugins: [],
rules: {},
//...
};
// .stylelintrc.js
module.exports = {
//...
extends: ["stylelint-config-prettier"],
plugins: [],
rules: {},
//...
};
3. 编译校验
很多 Source 问题会要求用户手动更改,所以需要及时提示。插件虽然会提示,但不会影响代码编译结果,所以需要在编译过程中校验
详见之前的 B - webpack 引入相关 loader、plugin 段落
4. 限制提交
修改 package.json
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
// src 目录下任意文件有改动,都使用 prettier --write 格式化之后再提交
"src/**/*.js": [
"eslint --fix",
"prettier --write",
"git add"
],
"src/**/*.css":[
"stylelint --fix",
"prettier --write",
"git add"
]
}
至此,风格统一相关的所有流程都已经覆盖到了!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。