定制开发初始化项目
// 定制开发创建一个空的 vue3-ts 项目,yarn create vite my-vue-app --template vue-ts// 安装依赖cd my-vue-app && yarn// 定制开发默认是没有创建git仓库的,定制开发这里我们初始化一下git init
- 1
- 2
- 3
- 4
- 5
- 6
定制开发这个模板是没有使用配置eslint
和prettier
的,定制开发接下来我们依次安装这些依赖。
集成
定制开发首先我们安装eslint
yarn add eslint -D
- 1
定制开发接下来初始化eslint
:
npx eslint --init
- 1
定制开发依次选择这些选项:
询问:How would you like to use ESLint? ...选择:To check syntax and find problems询问:What type of modules does your project use? ...选择:JavaScript modules (import/export)询问:Which framework does your project use? ...选择:Vue.js询问:Does your project use TypeScript?选择:yes询问:Where does your code run? ...选择:Browser (node定制开发服务端选择node项)询问:What format do you want your config file to be in? ...选择:JavaScript询问:Would you like to install them now with npm?选择:yes...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
到这一步,定制开发我们就已经安装了相关的依赖了,定制开发并且得到一个已配置好的.eslintrc.json
文件:
{ // 定制开发设置我们的运行环境为浏览器 + es2021 + node ,否则eslint在遇到 Promise,window定制开发等全局对象时会报错 "env": { "browser": true, "es2021": true, "node": true }, // 继承eslint定制开发推荐的规则集,vue定制开发基本的规则集,typescript的规则集 "extends": [ "eslint:recommended", "plugin:vue/essential", "plugin:@typescript-eslint/recommended" ], // 支持ts定制开发的最新语法 "parserOptions": { "ecmaVersion": "latest", "parser": "@typescript-eslint/parser", "sourceType": "module" }, // 添加vue和@typescript-eslint插件,增强eslint的能力 "plugins": [ "vue", "@typescript-eslint" ], "rules": { }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
定制开发然后我们为package.json
定制开发文件增加一个lint
命令
{ "scripts":{ // lint定制开发当前项目中的文件并且定制开发开启自动修复 "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix", }}
- 1
- 2
- 3
- 4
- 5
- 6
定制开发一切进行得非常顺利,定制开发然而当我们运行lint命令时,定制开发会发现不是我们想要的结果:
定制开发命令行在解析vue
文件会报parsing error
。这是因为,默认eslint
不会解析vue文件,定制开发所以我们需要一个额外定制开发的解析器来帮我们解析vue文件。
定制开发这一步本来是在我们继承plugin:vue/essential
的时候,定制开发默认为我们配置了的
定制开发但是我们后续又extend
了"plugin:@typescript-eslint/recommended"
,定制开发它又继承来自./node_modules/@typescript-eslint/eslint-plugin/dist/configs/base.js
定制开发而我们在配置文件中的extends顺序是:
{ "extends": [ "eslint:recommended", "plugin:vue/essential", "plugin:@typescript-eslint/recommended" ],}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
所以vue-eslint-parser
被@typescript-eslint/parser
覆盖了。定制开发这里我们只需要将外部的parser
改为vue-eslint-parser
,并且在parserOptions
定制开发中添加一个parser:@typescript-eslint/parser
属性即可,定制开发而这一步我们之前的配定制开发置文件里面已经有做了。这里可以查看
{ ... // 新增,解析vue文件 "parser":"vue-eslint-parser", "parserOptions": { "ecmaVersion": "latest", "parser": "@typescript-eslint/parser", "sourceType": "module" }, ...}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
两个parser
的区别在于,外面的parser
用来解析vue
文件,使得eslint
能解析标签中的内容,而
parserOptions中的
parser,即
@-eslint/parser`用来解析vue文件中``标签中的代码。
接下来我们继续运行 yarn run lint
,会发现又报错了:
- `节点要求有且只有一个根节点
- 找不到
defineProps
的定义
我们知道,这两个特性都是vue3引入的,问题可能出在我们的配置不支持vue3项目,翻阅./node_modules/eslint-plugin-vue
目录的相关配置,便可发现问题所在,eslint-plugin-vue
提供了几个预设的配置集。
没有vue3-
前缀的规则集对应vue2项目,vue3-
开头的对应vue3项目。而我们默认使用的是 vue/essential
这个规则集,由于我们是vue3项目,所以应该使用vue3的规则集,这里使用vue3-recommended
{ "extends": [ "eslint:recommended", -- "plugin:vue/essential", ++ "plugin:vue/vue3-recommended", "plugin:@typescript-eslint/recommended" ],}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
然后再运行,yarn run lint
,发现还是会报错。
因为defineProps是一个全局的预编译宏,eslint
不知其定义在哪里,所以需要在global
选项中将其标注出来,然而我阅读了eslint-plugin-vue
这个文件后,发现它已经预设了。
我们只需要在env中开启这个环境变量即可:
{ "env": { "browser": true, "es2021": true, "node": true, // 开启setup语法糖环境 ++ "vue/setup-compiler-macros":true },}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
然后我们再运行yarn run lint
,oh 谢天谢地,终于不报错了。
添加vscode-eslint 插件
就目前而言,我们能在命令行中使用eslint了,然而写一行代码就去运行下检测脚本实在太麻烦,好在我们可以结合vscode-eslint
插件使用。
在vscode
中安装好vscode-eslint
插件,它便会在我们写代码的时候对我们的脚本进行lint,我们就没必要再运行yarn run lint
了。同时,我们可以新建一个.vscode/settings.json
文件,为这个本项目开启自动修复
{ "editor.codeActionsOnSave": { "source.fixAll": true }}
- 1
- 2
- 3
- 4
- 5
这样一来,当你按下ctrl + s
保存的时候,eslint
便会智能地为你修复一些代码错误了。
安装依赖说明
- JavaScript 和 JSX 检查工具
- 目前比较流行的 JavaScript 代码规范
- 使用 ESLint 检查
.vue文件
的 ` 和 `` - 基于
prettier
代码风格的eslint
规则 - 禁用所有与格式相关的 eslint 规则,解决 prettier 与 eslint 规则冲突,确保将其放在
extends
队列最后,这样它将覆盖其他配置
集成
相比 eslint
而言 prettier
就会要温和一些了。 prettier 并没有提供太多的配置选项给我们选择,所以我们在网上随便找一份配置就行。
yarn add prettier -D
- 1
然后再项目根目录添加一份配置文件
// .prettierrc.jsmodule.exports = { printWidth: 80, //单行长度 tabWidth: 2, //缩进长度 useTabs: false, //使用空格代替tab缩进 semi: true, //句末使用分号 singleQuote: true, //使用单引号}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这是我的配置文件,如果需要更多的配置方法,可以参考。
然后再package.json中添加一个脚本
{ "scripts":{ "format": "prettier --write ./**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}" }}
- 1
- 2
- 3
- 4
- 5
当运行这个命令时,就会将我们项目中的文件都格式化一次。
一般而言,我们还需要集成 vscode-prettier
这个插件来完成自动保存格式化,在插件市场安装好了以后,在我们的.vscode/settings.json
中添加如下规则
{ "editor.formatOnSave": true, // 开启自动保存 "editor.defaultFormatter": "esbenp.prettier-vscode", // 默认格式化工具选择prettier}
- 1
- 2
- 3
- 4
这样一来,当我们在vscode写代码的时候,便会自动格式化了。
解决 eslint 和 prettier 的冲突
理想状态下,到这一步我们写代码的时候,eslint
和 prettier
会相互协作,既美化我们的代码,也修复我们质量不过关的代码。然而现实总是不那么完美,我们会发现某些时候,eslint
提示错误,我们修改了以后,屏幕会闪一下然后又恢复到报错状态,自动修复失效了。
这是因为eslint
有一部分负责美化代码的规则和 prettier
的规则冲突了,这里可以参考我的另外一篇博客,这里我们只给出方案。 用 eslint-config-prettier
提供的规则集来覆盖掉eslint
冲突的规则,并用eslint-plugin-prettier
来使eslint
使用prettier
的规则来美化代码。
yarn add eslint-config-prettier eslint-plugin-prettier -D
- 1
然后在 .eslintrc.json
中extends
的最后添加一个配置:
"extends": [ "eslint:recommended", "plugin:vue/vue3-recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended" // 新增,必须放在最后面 ],
- 1
- 2
- 3
- 4
- 5
- 6
然后我们重启一下vscode,就会发现冲突消失了,我们的自动修复和自动格式化也能相互协作了。
配置 husky + lint-staged
理论上,到上一步我们已经能使得我们的项目获得不错的开发规范约束了。然而仍然有可以改进的地方:
- 如果是在项目中途才接入
eslint + prettier
,如果对原来的代码使用yarn run lint
或者yarn run format
势必会带来大范围地改动,甚至会造成冲突。 - 对于一些不使用
vscode
编辑器,或者没有安装prettier
和eslint
插件的用户而言,他们不能享受到插件带来的协助,而他们的代码自然大概率是不符合规范的,不该被提交到代码库。
基于上述的顾虑,社区提供了 husky + lint-staged
的渐进式方案。 lint-staged
是一个只检测git
暂存区的lint
工具,husky
是一个用来给我们的项目添加git hook
的工具,git hook
是进行git操作会触发的脚本,例如:提交的时候会触发pre-commit
钩子,输入提交信息会触发commit-msg
钩子。 我们用husky
安装pre-commit
钩子,我们就可以在进行git commit
操作的时候,运行我们的脚本来检测待提交的代码是否规范,便可以只对暂存区的文件进行检查。
首先安装依赖
yarn add husky lint-staged -D
- 1
添加一个在package.json
中添加一条preinstall
脚本
{ "script":{ "prepare": "husky install" }}
- 1
- 2
- 3
- 4
- 5
prepare
脚本会在 yarn install
之后自动运行,这样依赖你的小伙伴clone
了你的项目之后会自动安装husky
,这里由于我们已经运行过 yarn install
了,所以我们需要手动运行一次yarn run prepare
,然后我们就会得到一个目录.husky
。
接下来我们为我们git仓库添加一个pre-commit
钩子,运行
npx husky add .husky/pre-commit "npx --no-install lint-staged"
- 1
这回在我们的.husky目录下生成一个pre-commit
的脚本
#!/bin/sh . "$(dirname "$0")/_/husky.sh"npx --no-install lint-staged
- 1
- 2
- 3
- 4
接下来我们配置lint-staged
,在package.json
中添加下面的配置信息。
{ "lint-staged": { "*.{js,vue,ts,jsx,tsx}": [ "prettier --write", "eslint --fix" ], "*.{html,css,less,scss,md}": [ "prettier --write" ] }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
这样之后,我们后续提交到暂存区的代码也就会被eslint+prettier
格式化和检查,进一步保证我们的代码规范。
配置 husky + lint-staged 后带来了新的eslint+prettier
冲突问题
问题背景
在Windows
笔记本上新拉完代码,在执行pre-commit
时,出现如下错误:
Delete `␍`eslint(prettier/prettier) 复制代码
- 1
- 2
下面是几种个人尝试过的解决方案:
解决方案
一、Crtl+S保存文件
按Crtl+S
保存当前报错文件,eslint
错误消失,这里我尝试网上提供的这个解决方案没有用,反而是我重启一下vscode编辑器报错没有了,不过总之这个方案是不行的
二、yarn run lint --fix
比上面省事,eslint
错误消失,但暂存区多了n个文件改动记录,对比Working tree
也没发现任何不同。
缺点:需要commit
所有文件,多余。
``
三、配置.prettierrc文件
在项目根目录下的.prettierrc
文件中写入即可。其实就是不让prettier
检测文件每行结束的格式.
"endOfLine": "auto"
- 1
- 2
缺点:不能兼容跨平台开发,从前端工程化上讲没有做到尽善尽美。
[参考资料:Why do I keep getting Delete ‘cr’
四、通过IDE一键切换
这是评论区掘金同道提供的方法,一般IDE下方状态栏会提供“切换行尾序列”的工具,如上图,一键切换到正确的行尾序列即可。
缺点:只能修复当前文件,不能解决整个项目报错的问题。
五、最佳方案
问题根源:
罪魁祸首是git
的一个配置属性:core.autocrlf
由于历史原因,windows
下和linux
下的文本文件的换行符不一致。
* Windows在换行的时候,同时使用了回车符CR(carriage-return character)和换行符LF(linefeed character) * 而Mac和Linux系统,仅仅使用了换行符LF * 老版本的Mac系统使用的是回车符CR
- 1
- 2
- 3
- 4
- 5
- 6
Windows | Linux/Mac | Old Mac(pre-OSX |
---|---|---|
CRLF | LF | CR |
‘\ ’ | ‘’ | ‘\r’ |
因此,文本文件在不同系统下创建和使用时就会出现不兼容的问题。
我的项目仓库中默认是Linux
环境下提交的代码,文件默认是以LF
结尾的(工程化需要,统一标准)。
当我用windows
电脑git clone
代码的时候,若我的autocrlf
(在windows
下安装git
,该选项默认为true
)为true
,那么文件每行会被自动转成以CRLF
结尾,若对文件不做任何修改,pre-commit
执行eslint
的时候就会提示你删除CR
。
现在可以理解ctrl+s
和yarn run lint --fix
方案为何可以修复eslint
错误了吧,因为Git
自动将CRLF
转换成了LF
。
最佳实践:
现在VScode
,Notepad++
编辑器都能够自动识别文件的换行符是LF
还是CRLF
。 如果你用的是windows
,文件编码是UTF-8
且包含中文,最好全局将autocrlf
设置为false
。
git config --global core.autocrlf false
- 1
- 2
注意:git
全局配置之后,你需要重新拉取代码。
由于我们组内全部使用的windows,所有直接配置.prettierrc
endOfLine: 'crlf', // Windows
- 1
配置文件总览
// package.json{ "name": "my-vue-app", "version": "0.0.0", "scripts": { "dev": "vite", "build": "vue-tsc --noEmit && vite build", "preview": "vite preview", "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix", "format": "prettier --write ./**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}", "prepare": "husky install" }, "dependencies": { "vue": "^3.2.25" }, "devDependencies": { "@types/node": "^16.3.1", "@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/parser": "^5.22.0", "@vitejs/plugin-vue": "^1.2.4", "@vitejs/plugin-vue-jsx": "^1.1.6", "@vue/compiler-sfc": "^3.0.5", "eslint": "^8.15.0", "eslint-config-prettier": "^8.5.0", "eslint-config-standard": "^17.0.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-vue": "^8.7.1", "husky": "^8.0.1", "lint-staged": "^12.4.1", "prettier": "^2.6.2", "typescript": "^4.3.2", "vite": "^2.8.6", "vue-tsc": "^0.33.9" } "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.7.0", "@typescript-eslint/parser": "^5.7.0", "@vitejs/plugin-vue": "^2.0.0", "eslint": "^8.4.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-vue": "^8.2.0", "husky": "^7.0.4", "lint-staged": "^12.1.3", "prettier": "^2.5.1", "typescript": "^4.4.4", "vite": "^2.7.2", "vue-tsc": "^0.29.8" }, "lint-staged": { "*.{js,vue,ts,jsx,tsx}": [ "prettier --write", "eslint --fix" ], "*.{html,css,less,scss,md}": [ "prettier --write" ] }}// .eslintrc.jsmodule.exports = { // 设置我们的运行环境为浏览器 + es2021 + node ,否则eslint在遇到 Promise,window等全局对象时会报错 env: { browser: true, es2021: true, node: true, // 开启setup语法糖环境 'vue/setup-compiler-macros': true, }, // 继承eslint推荐的规则集,vue基本的规则集,typescript的规则集 extends: [ 'plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', 'standard', // 格式化规范 'plugin:prettier/recommended', // 新增,必须放在最后面,prettier规则覆盖与eslint冲突的规则 'prettier', // 避免vue 与 prettier冲突 'eslint:recommended', ], // 定义ESLint的解析器 parser: 'vue-eslint-parser', // 支持ts的最新语法 parserOptions: { ecmaVersion: 'latest', parser: '@typescript-eslint/parser', sourceType: 'module', }, // 添加vue和@typescript-eslint插件,增强eslint的能力 plugins: ['vue', '@typescript-eslint', 'prettier'], rules: { // "off"或者0 //关闭规则关闭 // "warn"或者1 //在打开的规则作为警告(不影响退出代码) // "error"或者2 //把规则作为一个错误(退出代码触发时为1) 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // 解析vue模板错误规则 'vue/no-parsing-error': [ 2, { 'x-invalid-end-tag': false, 'missing-semicolon-after-character-reference': false, }, ], 'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }], // 允许使用短路、三目 'no-param-reassign': ['error', { props: false }], // 函数形参可修改 'vue/multi-word-component-names': 'off', // eslint-plugin-vue @8版本中新增了不少的规则,第一条就是 **‘vue/multi-word-component-names’: ‘error’,**所有index.vue 会报错,解决方法: 'no-new-object': 'off', // 关闭不可以new对象的规则 'no-explicit-any': 'off', // 可以使用any,但请尽量不使用any '@typescript-eslint/no-explicit-any': 'off', // ts可以使用any 'vue/v-on-event-hyphenation': 'off', // 关闭事件命名样式需要用连字符 },};// .prettierrc.js/** tips: endOfLine: 'crlf' 因为eslint和prettier就这个问题上不一致互相冲突,我们这里统一使用Windows系统的. 如果是有mac使用需求的请使用git config --global core.autocrlf false解决 */module.exports = { printWidth: 100, //单行长度 tabWidth: 2, //缩进长度 useTabs: false, //使用空格代替tab缩进 semi: true, //句末使用分号 singleQuote: true, //使用单引号 endOfLine: 'crlf', // Windows};// .vscode/settings.json{ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { "source.fixAll": true }}// .husky/pre-commit#!/bin/sh. "$(dirname "$0")/_/husky.sh"npx --no-install lint-staged
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
鸣谢
参考:
感谢各位开源作者