首先简单列一下,在一个新项目初始阶段,我们应该做些什么。
本篇将以vue3 + vite2 + ts + pnpm
来搭建我们的项目,这是大厂的企业级项目架构
系列的第一篇,我会先讲解项目的搭建和代码规范。跟着我一步步慢慢搭建出一个大厂该有的企业级项目。
耐心看完,你将会了解到在项目开始阶段是如何落地代码规范的,我们将使用prettier + eslint + stylelint + lint-staged
来规范我们的代码。
node版本 16.16.0
1.使用vite创建项
// 不同版本或者包管理工具的创建命令可看vite官网
pnpm create vite
创建完后的项目结构如下
├── .vscode存放本项目推荐安装的vscode插件等
├── README.md
├── .gitignore 提交到git时需要忽略的文件
├── index.html 页面模板
├── node_modules 安装的依赖
├── package.json
├── pnpm-lock.yaml
├── public 静态资源目录,放在这个目录下的资源只会被简单地复制,不会被vite处理,需要绝对路径引用
│ └── vite.svg
├── src源码目录
│ ├── App.vue
│ ├── assets
│ │ └── vue.svg
│ ├── components
│ │ └── HelloWorld.vue
│ ├── main.ts
│ ├── style.css
│ └── vite-env.d.ts
├── tsconfig.jsonts配置文件,针对我们的源码的ts配置文件
├── tsconfig.node.json 专门针对vite.config.ts的ts配置文件,上面的tsconfig.json引用了这个文件
└── vite.config.ts vite配置文件
着重解释一下两个文件
初始化的时候项目依赖如下(package.json)
"dependencies": {"vue": "^3.2.37" // 用vue,那肯定得依赖vue了
},
"devDependencies": {"@vitejs/plugin-vue": "^3.0.0", // vite的vue插件,用来解析vue的单文件组件"typescript": "^4.6.4", // 用到ts,就需要typescript"vite": "^3.0.0", // 用vite做构建"vue-tsc": "^0.38.4" //针对vue单文件组件,使用vue-tsc做类型检查
}
全局类型声明文件vite-env.d.ts
///
// 上面是三斜线指令,用于告诉编译器在编译的过程中要引用额外的文件
// 如果是types属性,则用于声明对另一个库的依赖,如上面,则是对vite这个库下的cliend.d.ts的引用,具体可见node_modules
// 如果是path属性,则用于声明对另一个文件的依赖
// 因为这是一个全局的声明文件,所以使用了三斜线指令,如果用了import,则文件就会变成了模块声明文件了
// 下面的意思是是,全局定义一个模块,当import xxx from 'xx.vue';的方式引入模板文件的时候,编译器不会报错
// 且引入的类型是DefineComponent类型
declare module '*.vue' {import type { DefineComponent } from 'vue'const component: DefineComponent<{}, {}, any>export default component
}
创建好项目之后,就可以接入代码规范了
在项目根目录下增加.editorConfig,内容如下
root = true # 表明这是最顶层的editorConfig配置文件
[*] # 表示适用于所有文件
charset = utf-8 # 字符集 utf-8
indent_style = tab # 缩进风格 space|tab
indent_size = 4 # 缩进大小是几个空格
end_of_line = lf # 控制换行类型 lf|cr|crlf
trim_trailing_whitespace = true # 去除行首任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false
增加配置后安装EditorConfig for VS Code插件即可
在根目录下创建.prettierrc,内容如下
module.exports = {printWidth: 100, // 1行最多100个字符semi: true, // 要分号结尾//vueIndentScriptAndStyle: true, // 配置了这个,vue模板文件里面的script 和 style 内就会缩进singleQuote: true, // 使用单引号// trailingComma: 'all', // 末尾要有逗号proseWrap: 'preserve', // 针对markdown文件,如果超过了最大字符数 always 换行| never 不换行| preserve 原样展示htmlWhitespaceSensitivity: 'strict', // html空格敏感endOfLine: 'auto', // 行结尾,跟editorConfig的end_of_line一样
};
增加.prettierignore,忽略不需要格式化的内容
/dist/*
/node_modules/**
pnpm-lock.yaml
**/*.svg
**/*.sh
/public/*
增加配置后,安装vscode插件 Prettier - Code formatter,如果想要保存的时候自动格式化,则可以添加配置,为了不影响其他项目,可以在自己项目下的.vscode目录下增加settings.json
{"editor.formatOnSave": true
}
同时,需要安装prettier,并在package.json中增加脚本,以便后续提交前做格式化
pnpm i -D prettier
{"scripts": {"prettier": "npx prettier --write ./src/**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}"}
}
prettier只是处理了代码格式,eslint可以保障我们的代码质量,帮我们规避一些低级错误,如果有不了解eslint的,这里建议先看完我的另外一篇文章,有详细讲解eslint的各种配置,看完这篇文章,我不信你还对eslint一知半解,看完之后你自然就明白为什么会那么配置了。
在项目根目录下增加.eslintrc.js
因为要使用eslint以及相应的解释器和插件,使用前需先安装,具体的作用可看配置里面的注释
pnpm i -D eslint vue-eslint-parser eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin
因为这个项目用的vue3 + ts,所以这里就只针对vue|ts|tsx这几种文件进行校验了。
具体配置和解释如下:
module.exports = {root: true, // 让eslint不要往父级去继续查找配置parser: 'vue-eslint-parser', // 因为需要校验vue模板文件里面的html,所以这里使用vue-eslint-parser这个parser作为解释器parserOptions: {// 因为上面使用了vue-eslint-parser,它只会校验模板里面的html,对于script里面的ts,仍然需要其他解释器去处理// 所以vue-eslint-parser就预留了parser选项让我们传进去解释器parser: '@typescript-eslint/parser',ecmaVersion: 2020, // 这里我们使用es2020的语法,所以在这里指定一下sourceType: 'module', // 我们的文件都是模块而非script,所以这里指定成moduleecmaFeatures: {jsx: true // 项目用到了jsx的写法,所以要开启jsx},},env: {browser: true, // 项目运行在浏览器环境,所以指定浏览器相关的全局变量node: true, // 因为某些文件,如某些构建脚本之类的运行在node环境,所以这里也指定了node相关的全局变量//虽然上面的ecmaVersion指定支持2020的语法,但并不意味着支持es6最新的全局变量和类型,比如Set,所以在这里指定一下。//需要注意的是,在这里设置es6:true,就会自动启用es6语法,但是上面设置ecmaVersion:6并不会启用es6全局变量es6: true,},extends: [// 使用eslint-plugin-vue的vue3-recommended规则集校验vue模板'plugin:vue/vue3-recommended',// 使用@typescript-eslint/eslint-plugin来校验ts,注意需配合@typescript-eslint/parser使用'plugin:@typescript-eslint/recommended',]
};
配置完可以先命令行尝试一下是否生效,在package.json中增加脚本如下,然后写一些不符合规则的代码测试一下,比如使用一个未定义的变量
{"scripts": {"lint": "npx eslint ./src --ext .vue,.ts,.tsx","lint:fix": "npx eslint ./src --ext .vue,.ts,.tsx --fix"}
}
确保配置没问题后,可以在vscode中安装eslint插件,同时可以把这个插件加入到工作空间的推荐插件(.vscode/extensions.json)里面,然后在.vscode/settings.json里面增加如下配置:
{// onSave:保存的时候进行校验|onType:输入的时候就进行lint"eslint.run": "onSave",// 假如你想保存的时候帮你修复错误,则可以添加如下提示,但是因为本人想自己养成好的编码习惯,所以就不采用保存时修复了// "editor.codeActionsOnSave": {// "source.fixAll.eslint": true// }
}
配置完之后就可以在编辑器里面有相应的提示了,如果没有,则可能你需要升级你的vscode的eslint插件到最新版本,或者重启vscode
假如你同时在vscode插件里吗配置了prettier和eslint的保存时修复,则可能因为规则不一样而引起冲突,因为prettier负责代码格式,eslint则同时可以支持代码格式和代码质量,这个时候我们希望代码格式由prettier负责就好,而eslint则只需负责代码质量。
解决方案是借助eslint-config-prettier
和eslint-plugin-prettier
。
eslint-config-prettier
作用是关闭eslint中与prettier冲突的规则。eslint-plugin-prettier
作用是让eslint用prettier来格式化,相当于当代码格式不符合prettier的配置的时候,也可以报错一个eslint错误修改配置如下:
module.exports = {extends: [// 新增如下的配置,实际上就是eslint-plugin-prettier的推荐规则集// 而eslint-plugin-prettier实际上就是依赖eslint-config-prettier来关闭冲突的,使用前需同时安装这两个包'plugin:prettier/recommended']
};
有兴趣的同学可以看一下下面eslint-plugin-prettier是如何实现的,没兴趣的可以略过
// eslint-plugin-prettier.js
module.exports = {// plugin:prettier/recommended 就是加载这个configs: {recommended: {// 里面依赖了eslint-config-prettierextends: ['prettier'],plugins: ['prettier'],rules: {'prettier/prettier': 'error','arrow-body-style': 'off','prefer-arrow-callback': 'off',},},},// ...rules
}
最后,完整的eslint配置如下:
module.exports = {root: true, // 让eslint不要往父级去继续查找配置parser: 'vue-eslint-parser', // 因为需要校验vue模板文件里面的html,所以这里使用vue-eslint-parser这个parser作为解释器parserOptions: {// 因为上面使用了vue-eslint-parser,它只会校验模板里面的html,对于script里面的ts,仍然需要其他解释器去处理// 所以vue-eslint-parser就预留了parser选项让我们传进去解释器parser: '@typescript-eslint/parser',ecmaVersion: 2020, // 这里我们使用es2020的语法,所以在这里指定一下sourceType: 'module', // 我们的文件都是模块而非script,所以这里指定成modulejsxPragma: 'React',ecmaFeatures: {jsx: true, // 项目用到了jsx的写法,所以要开启jsx},},env: {browser: true, // 项目运行在浏览器环境,所以指定浏览器相关的全局变量node: true, // 因为某些文件,如某些构建脚本之类的运行在node环境,所以这里也指定了node相关的全局变量//虽然上面的ecmaVersion指定支持2020的语法,但并不意味着支持es6最新的全局变量和类型,比如Set,所以在这里指定一下。//需要注意的是,在这里设置es2020:true,就会自动启用es2020语法,但是上面设置ecmaVersion:2020并不会启用es2020全局变量es2020: true,
},
extends: ['eslint:recommended',// 使用eslint-plugin-vue的vue3-recommended规则集校验vue模板'plugin:vue/vue3-recommended',// 使用@typescript-eslint/eslint-plugin来校验ts,注意需配合@typescript-eslint/parser使用'plugin:@typescript-eslint/recommended',// 用eslint-plugin-prettier的推荐规则集来解决prettier和eslint的冲突// 而eslint-plugin-prettier实际上就是依赖eslint-config-prettier来关闭冲突的// eslint-config-prettier作用是关闭eslint中与prettier冲突的规则。// eslint-plugin-prettier作用是让eslint用prettier来格式化,相当于当代码格式不符合prettier的配置的时候,也可以报错一个eslint错误'plugin:prettier/recommended',
],
rules: {'vue/multi-word-component-names': 'off','vue/attributes-order': 'off','vue/one-component-per-file': 'off','vue/html-closing-bracket-newline': 'off','vue/max-attributes-per-line': 'off','@typescript-eslint/ban-ts-ignore': 'off','@typescript-eslint/explicit-function-return-type': 'off','@typescript-eslint/no-explicit-any': 'off',// 剩下具体的规则可自行根据需要关闭
},
};
我们还需要在根目录增加一个.eslintignore来忽略那些无需校验的文件,如下:
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
/bin
至此,就完成了对项目代码规范相关的配置了
此时在开发阶段假如我们的代码不符合规范,vscode编辑器就会给我们相应的提示了,且也支持保存时自动修复了,但是这也不能确保每个人都会遵守这些规则,也许有些人就是不想装这些插件,直接把不合规范的代码push了。所以我们需要再加一些限制,防止没通过eslint检测的代码直接被push到代码仓库。
要想实现上述的限制,我们可以利用git hooks
,在commit
之前执行一些动作,比如eslint校验和修复,没通过检验则禁止提交。
我们借助husky
和lint-staged
来实现提交前校验。
你可以跟着npm上写的步骤来安装和初始化husky,也可以使用husky-init
命令来初始化,两种方式的作用是一样的,我这里使用husky-init
,如下所示
pnpm i -D husky-init
npx husky-init
执行完命令husky-init
之后,它就会自动帮我们安装husky,并创建.husky目录,在该目录下会创建一个pre-commit
的hook,我们只需要修改这个hook里面的命令,改成我们想要执行的操作即可,如下所示
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# 这里的lint脚本是在package.json里面配置的,执行这个就相当于执行npx eslint
npm run lint
配置完成后,我们每次commit前就会执行eslint来校验我们的代码了,只有校验通过才可以commit。
但是此时效率是非常低的,因为我们每次commit都会对项目的所有文件执行lint,如果项目很大的话,效率就很低了。我们其实只想校验本次改动的代码,这个时候lint-staged
就派上用场了。
lint-staged
可以让husky的hook触发的命令只作用于我们本次通过git add
添加到暂存区的那些文件,可以大大地优化我们的项目。
老规矩,使用前先安装
pnpm i -D lint-staged
然后修改package.json如下
{"scripts": {"lint:lint-staged": "lint-staged"},// 针对不同类型的文件做不同的处理"lint-staged": {"*.{vue,js,jsx,ts,tsx}": ["prettier --write",// 这里就不fix了,只校验,当然你也可以选择fix"eslint"],"package.json": ["prettier --write"],"*.md": ["prettier --write"]}
}
然后将公共pre-commit
这个hook里的脚本改成npm run lint:lint-staged
,此时就可以commit一下测试有没有生效了
除了上述的对html和js的代码的规范,还可以使用stylelint
对样式代码进行规范,当然,这个就看团队喜好了,可能很多团队会觉得这个的重要性并没有那么高,就没有强制对样式代码作出规范。不过其实使用stylelint
除了规范样式代码,还有个有点,就是可以保证我们的css属性的书写顺序。好的书写顺序可以减少浏览器回流,提高渲染性能,所以我们本次的工程也要接入stylelint。
因为eslint的配置我单独写过一篇文章,但是stylelint没有,所以这里会写的相对详细一点,让大家明白为什么要这么配置。
stylelint的使用跟eslint差不多,首先要安装stylelint和相关的包,相关包的作用如下:
stylelint-config-standard
: stylelint官方的规则集,它继承了stylelint-config-recommended
stylelint-config-prettier
: 用来禁用掉stylelint中与格式相关的规则, 防止跟eslint冲突pnpm i -D stylelint stylelint-config-standard
在项目根目录下创建stylelint.config.js
,配置内容如下,详细配置看里面的注释
module.exports = {// 继承官方的规则集,同时stylelint-config-prettier放在后面,禁用格式相关的规则,覆盖掉前面的extends: ['stylelint-config-standard', 'stylelint-config-prettier'],// 忽略这些后缀的文件ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
};
这个时候已经可以对css文件进行校验了,但还不能对.vue
文件中style标签里面的样式进行校验,此时需要其他的语法解释器来处理.vue
文件,类似于eslint中parser的概念,这里我们用postcss-html
来处理类html文件(html、vue等),使stylelint可以对这些文件做校验。
安装postcss和postcss-html
pnpm i -D postcss postcss-html
修改配置如下
module.exports = {// 继承官方的规则集,同时stylelint-config-prettier放在后面,禁用格式相关的规则,覆盖掉前面的extends: ['stylelint-config-standard', 'stylelint-config-prettier'],// 忽略这些后缀的文件ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],// 加上这个,就可以处理vue模板customSyntax: 'postcss-html'
};
但此时像上面的这么改,虽然可以校验.vue
里面的模板文件,但是你会发现css文件又校验不了了,所以我们可以像下面这么写,单独针对.vue
文件做配置
module.exports = {// 继承官方的规则集,同时stylelint-config-prettier放在后面,禁用格式相关的规则,覆盖掉前面的extends: ['stylelint-config-standard', 'stylelint-config-prettier'],// 忽略这些后缀的文件ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],// 通过overrides单独针对某些类型的文件处理overrides: [{// 针对html和vue文件进行处理files: ['*.html', '**/*.html', '**/*.vue'],// 在这里配置针对vue文件使用postcss-html作为语法解析器customSyntax: 'postcss-html'},],
};
此时就既可以校验vue文件又可以校验css文件了,不过上面的写法其实还可以简化成像下面一样
module.exports = {// 使用前需先安装// stylelint-config-recommended-vue也继承了stylelint-config-standard// 所以这里就不用再写stylelint-config-standard了// 同时stylelint-config-recommended-vue里面也是像上面一样通过override为vue文件指定了解释器// stylelint-config-recommended-vue还帮我们增加了几条针对vue文件里面的样式校验规则extends: ['stylelint-config-recommended-vue', 'stylelint-config-prettier'],// 忽略这些后缀的文件ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
};
改成像上面的配置之后,可以先验证一下是否可以校验css和vue文件
npx stylelint ./src
但是因为我们的项目打算使用less
,所以我们还需要针对.less
文件指定postcss-less
,假如你项目用的是scss
那你就指定成postcss-scss
就可以了
module.exports = {extends: ['stylelint-config-recommended-vue', 'stylelint-config-prettier'],ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],overrides: [{// 针对less件进行处理files: ['*.less', '**/*.less'],// 在这里配置针对less文件使用postcss-less作为语法解析器customSyntax: 'postcss-less',},],
};
通过上面的配置之后,你就可以同时对vue|css|less
做校验了。
此时,我们还有很重要的一步,就是要规范我们的css属性的书写顺序。这个时候我们可以通过stylelint-order
这个插件来处理,这个插件使用方式如下
module.exports = {
//... 前面的省略plugins: ['stylelint-order'],rules: {// css属性的书写顺序需按照下面的来"order/properties-order": ["position","top","right","bottom","left",// ....]},
};
但是像上面那么写就有点太长了,此时我们可以使用第三方开源的包stylelint-config-rational-order
,使用他们封装好的推荐顺序,所以上面可以改成如下的写法:
module.exports = {extends: [// 注意stylelint 14的版本里在vue文件中可能会出现Unknown word (CssSyntaxError)这个报错// 此时可以使用stylelint-config-standard-scss这个包来解决这个报错// 当然你也可以选择降低版本来解决'stylelint-config-standard-scss',// 'stylelint-config-standard',// 下面这个包已经包含了stylelint-config-standard,所以上面就可以注释掉了'stylelint-config-recommended-vue',// 关闭stylelint中校验格式的规则,以免跟prettier冲突'stylelint-config-prettier',// 下面是基于stylelint-order的规范css属性书写顺序的包,这样我们就不用自己在rules中手动指定了'stylelint-config-rational-order',],ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],rules: {// 使用tab缩进indentation: 'tab',// 如果你有想配置的规则或者关闭的规则,只需在这里配置即可},overrides: [// 上面的extends里的stylelint-config-recommended-vue已经帮我们指定了vue文件的解释器了,所以下面的也可以注释掉了// {// // 针对html和vue文件进行处理// files: ['*.html', '**/*.html', '**/*.vue'],// // 在这里配置针对vue文件使用postcss-html作为语法解析器// customSyntax: 'postcss-html'// },{files: ['**/*.less'],customSyntax: 'postcss-less',},],
};
我们需要再增加一个stylelint的忽略配置.stylelintignore
如下
/dist/*
/public/*
public/*
/node_modules/*
到这里你就可以在package.json中增加样式的校验脚本了,如下
{"scripts": {"lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\""},
}
确保脚本可以正常运行之后,我们再安装一个stylelint的vscode插件,这样就可以在输入的时候就给我们提示,也可以保存时给我们修复了,将这个插件也加入到.vscode/extensions.json里作为共享的配置,并在.vscode/settings.json里面增加如下配置开启校验
{"stylelint.enable": true,"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],"editor.codeActionsOnSave": {"source.fixAll.stylelint": true}
}
最后,我们同样需要在commit之前校验样式,所以我们在上面写好的lint-stage的基础上修改成如下配置:
// package.json
{"scripts": {"lint:lint-staged": "lint-staged"},// 针对不同类型的文件做不同的处理"lint-staged": {"*.{js,jsx,ts,tsx}": [// 这里就不fix了,只校验,当然你也可以选择fix"eslint""prettier --write",],"*.vue": [// 不fix逻辑代码"eslint","prettier --write","stylelint --fix"],"package.json": ["prettier --write"],"*.md": ["prettier --write"]}
}
做完这一步之后,关于代码规范相关的就配置完了,
代码规范是前端项目架构的重要一环。为了避免篇幅过长,本篇先讲解了项目初始化阶段是如何使用prettier + eslint + stylelint
来规范我们的代码的,同时使用husky + stylelint
来做提交前的代码校验。接下来的系列文章我将继续完善项目,讲解关于提交规范、样式方案、基础能力(网络请求、路由、数据存储)等内容,有兴趣的同学可以关注一下,如果可以的话,点个赞支持一下吧。
整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。
有需要的小伙伴,可以点击下方卡片领取,无偿分享