【大厂企业级项目架构】之项目搭建和代码规范

首先简单列一下,在一个新项目初始阶段,我们应该做些什么。

  • 首先是技术选型,比如框架选型(Vue|React等),构建工具,包管理工具等等。
  • 代码规范,提交规范
  • 环境区分,包括开发、测试、生产的构建区分等
  • 样式方案,图标方案,布局方案,组件库等
  • 基础能力,包括路由、状态管理、持久化、网络请求等
  • mock方案
  • 根据自身的业务类型来封装相应的公共模块
  • 单元测试
  • 自动化构建部署

本篇将以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统一不同编辑器的编码风格

在项目根目录下增加.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插件即可

使用prettier格式化代码

在根目录下创建.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}"}
} 

使用eslint保证代码质量

prettier只是处理了代码格式,eslint可以保障我们的代码质量,帮我们规避一些低级错误,如果有不了解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

解决prettier和eslint的冲突

假如你同时在vscode插件里吗配置了prettier和eslint的保存时修复,则可能因为规则不一样而引起冲突,因为prettier负责代码格式,eslint则同时可以支持代码格式和代码质量,这个时候我们希望代码格式由prettier负责就好,而eslint则只需负责代码质量。

解决方案是借助eslint-config-prettiereslint-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 

至此,就完成了对项目代码规范相关的配置了

使用husky和lint-staged

此时在开发阶段假如我们的代码不符合规范,vscode编辑器就会给我们相应的提示了,且也支持保存时自动修复了,但是这也不能确保每个人都会遵守这些规则,也许有些人就是不想装这些插件,直接把不合规范的代码push了。所以我们需要再加一些限制,防止没通过eslint检测的代码直接被push到代码仓库。

要想实现上述的限制,我们可以利用git hooks,在commit之前执行一些动作,比如eslint校验和修复,没通过检验则禁止提交。

我们借助huskylint-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的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

你可能感兴趣的:(架构,代码规范,vue.js)