30 从 0 搭建 Vite 3 Vue 3 前端工程化项目 - AnnGreen1/article GitHub Wiki
mp.weixin.qq.com威廉王子 前端Q
前端Q
Vue 3 正式版已经发布有一段时间了,随着 Vite 脚手架注定成为下一代前端工具链,许多用户都想基于 Vite 来构建 Vue 项目,如果想基于 Vite 构建 Vue 3 项目,社区模板完全满足您的需求,如果想构建 Vite 3 + Vue 3 + JavaScript 项目,那社区模板不太能满足您的需求,因为社区模板提供 Vue 3 项目几乎是基于 Vite 2 + TypeScript 构建,对于不熟悉 TypeScript 语言的用户不是很友好,因此接下来从 0 开始手把手带大家搭建一套规范的 Vite 3 + Vue 3 + JavaScript 前端工程化项目环境。
本文章篇幅较长,从以下几个方面展开:
-
基础搭建
-
代码规范
-
提交规范
-
自动部署
本项目完整代码托管在 GitHub 仓库[1],欢迎点亮小星星 🌟🌟
-
⚡️ Vite 3 - 构建工具(就是快!)
-
🖖 Vue 3 - 渐进式 JavaScript 框架
-
🚦 Vue Router - 官方路由管理器
-
📦 Pinia - 值得你喜欢的 Vue Store
-
💻 TDesign - TDesign 适配桌面端的组件库
-
🎨 Less - CSS 预处理器
-
🔗 Axios - 一个基于 promise 的网络请求库,可以用于浏览器和 node.js
-
🧰 Husky + Lint-Staged - Git Hook 工具
-
🛡️ EditorConfig + ESLint + Prettier + Stylelint - 代码规范
-
🔨 Commitizen + Commitlint - 提交规范
-
💡 GitHub Actions - 自动部署
确保你安装了最新版本的 Node.js,然后在命令行中运行以下命令:
# npm6.x
npm create vite@latest vite-vue-js-template --template vue
# npm7+,extra double-dash is needed:
npm create vite@latest vite-vue-js-template -- --template vue
# yarn
yarn create vite vite-vue-js-template --template vue
# pnpm
pnpm create vite vite-vue-js-template --template vue
这一指令将会安装并执行 create-vite[19],它是一个基本模板快速启动项目工具。
初始化项目
在项目被创建后,通过以下步骤安装依赖并启动开发服务器:
#打开项目cd<your-project-name>#安装依赖npminstall#启动项目npmrundev复制代码
启动项目
Vite 配置文件 vite.config.js
位于项目根目录下,项目启动时会自动读取。
本项目针对公共基础路径、自定义路径别名、服务器选项、构建选项等做了如下基础配置:
import{defineConfig}from'vite';import{resolve}from'path';importvuefrom'@vitejs/plugin-vue';exportdefaultdefineConfig({base:'./',plugins:[vue(),],resolve:{alias:{'@':resolve(__dirname,'./src'),},},server:{//是否开启httpshttps:false,//端口号port:3000,//监听所有地址host:'0.0.0.0',//服务启动时是否自动打开浏览器open:true,//允许跨域cors:true,//自定义代理规则proxy:{},},build:{//设置最终构建的浏览器兼容目标target:'es2015',//构建后是否生成sourcemap文件sourcemap:false,//chunk大小警告的限制(以kbs为单位)chunkSizeWarningLimit:2000,//启用/禁用gzip压缩大小报告reportCompressedSize:false,},});复制代码
关于 Vite 更多配置项及用法,请查看 Vite 官网 vitejs.dev/config/[20] 。
├── dist/└── src/ ├── api/ // 接口请求目录 ├── assets/ // 静态资源目录 ├── common/ // 通用类库目录 ├── components/ // 公共组件目录 ├── router/ // 路由配置目录 ├── store/ // 状态管理目录 ├── style/ // 通用样式目录 ├── utils/ // 工具函数目录 ├── views/ // 页面组件目录 ├── App.vue ├── main.js├── tests/ // 单元测试目录├── index.html├── jsconfig.json // JavaScript 配置文件├── vite.config.js // Vite 配置文件└── package.json复制代码
npmivue-router@4复制代码
在 src/router
目录下新建 index.js
文件与 modules
文件夹
└── src/ ├── router/ ├── modules/ // 路由模块 ├── index.js // 路由配置文件复制代码
关于路由表,建议根据功能的不同来拆分到 modules
文件夹中,好处是:
-
方便后期维护
-
减少 Git 合并代码冲突可能性
exportdefault[{path:'/',name:'home',component:()=>import('@/views/HomeView.vue'),},{path:'/about',name:'about',component:()=>import('@/views/AboutView.vue'),},];复制代码
import{createRouter,createWebHistory}from'vue-router';importbaseRoutersfrom'./modules/base';constroutes=[...baseRouters];constrouter=createRouter({history:createWebHistory(import.meta.env.BASE_URL),routes,scrollBehavior(){return{el:'#app',top:0,behavior:'smooth',};},});exportdefaultrouter;复制代码
根据路由配置的实际情况,需要在 src
下创建 views
目录,用来存储页面组件。
在 main.js
文件中挂载路由配置
import{createApp}from'vue';importAppfrom'./App.vue';importrouterfrom'./router';createApp(App).use(router).mount('#app');复制代码
npmipinia复制代码
在 src/store
目录下新建 index.js
文件与 modules
文件夹
└── src/ ├── store/ ├── modules/ // 仓库模块 ├── index.js // 仓库配置文件复制代码
import{defineStore}from'pinia';exportconstuseCounterStore=defineStore('counter',{state:()=>({count:1,}),actions:{accumulate(){this.count++;},},});复制代码
import{createPinia}from'pinia';conststore=createPinia();exportdefaultstore;export*from'./modules/counter';复制代码
开发中需要将不同功能所对应的状态,拆分到不同的 modules
,好处如同路由模块一样。
在 main.js
文件中挂载 Vuex
配置
import{createApp}from'vue';importAppfrom'./App.vue';importstorefrom'./store';importrouterfrom'./router';createApp(App).use(router).use(store).mount('#app');复制代码
npmitdesign-vue-next复制代码
import{createApp}from'vue';importTDesignfrom'tdesign-vue-next';//引入组件库全局样式资源import'tdesign-vue-next/es/style/index.css';constapp=createApp(App);app.use(TDesign);复制代码
使用 unplugin-vue-components
和 unplugin-auto-import
来实现自动导入:
npminstallunplugin-vue-componentsunplugin-auto-import-D复制代码
在 Vite 对应的配置文件 vite.config.js
添加上述插件:
importAutoImportfrom'unplugin-auto-import/vite';importComponentsfrom'unplugin-vue-components/vite';import{TDesignResolver}from'unplugin-vue-components/resolvers';exportdefault{plugins:[AutoImport({resolvers:[TDesignResolver({library:'vue-next'})],}),Components({resolvers:[TDesignResolver({library:'vue-next'})],}),],};复制代码
npmiaxios复制代码
在 utils
目录下创建 request.js
文件,配置好适合自己业务的请求拦截和响应拦截:
└── src/ ├── api // 接口 ├── utils/ ├── request.js // axios 请求库二次封装复制代码
importaxiosfrom'axios';//创建请求实例constinstance=axios.create({baseURL:'/api',//指定请求超时的毫秒数timeout:1000,//表示跨域请求时是否需要使用凭证withCredentials:false,});//前置拦截器(发起请求之前的拦截)instance.interceptors.request.use((config)=>{/***在这里一般会携带前台的参数发送给后台,比如下面这段代码:*consttoken=getToken()*if(token){*config.headers.token=token*}*/returnconfig;},(error)=>{returnPromise.reject(error);},);//后置拦截器(获取到响应时的拦截)instance.interceptors.response.use((response)=>{/***根据你的项目实际情况来对response和error做处理*这里对response和error不做任何处理,直接返回*/returnresponse;},(error)=>{const{response}=error;if(response&&response.data){returnPromise.reject(error);}const{message}=error;console.error(message);returnPromise.reject(error);},);//导出常用函数/***@param{string}url*@param{object}data*@param{object}params*/exportfunctionpost(url,data={},params={}){returninstance({method:'post',url,data,params,});}/***@param{string}url*@param{object}params*/exportfunctionget(url,params={}){returninstance({method:'get',url,params,});}/***@param{string}url*@param{object}data*@param{object}params*/exportfunctionput(url,data={},params={}){returninstance({method:'put',url,params,data,});}/***@param{string}url*@param{object}params*/exportfunction_delete(url,params={}){returninstance({method:'delete',url,params,});}exportdefaultinstance;复制代码
之后在 api
文件夹中以业务模型对接口进行拆分,举个例子,将所有跟用户相关接口封装在 User
类中,此类称作用户模型。
在 User
类中比如有登录、注册、获取用户信息等方法,如果有业务逻辑变动,只需要修改相关方法即可。
import{post}from'@/utils/request';exportdefaultclassUser{/***登录*@param{String}username用户名*@param{String}password密码*@returns*/staticasynclogin(username,password){returnpost('/login',{username,password,});}}复制代码
把每个业务模型独立成一个 js 文件,声明一个类通过其属性和方法来实现这个模型相关的数据获取,这样可以大大提升代码的可读性与可维护性。
在需要使用接口的地方,引入对应的业务模型文件,参考如下:
<script>importUserfrom'@/api/user';exportdefault{data(){return{username:'',password:'',};},methods:{asynclogin(){constres=awaitUser.login(this.username,this.password);console.log(res);},},};</script>复制代码
本项目使用 CSS 预处理器 Less,直接安装为开发依赖即可。
Vite 内部已帮我们集成了相关的 loader
,不需要额外配置。
npmiless-D复制代码
在 <style></style>
样式标签中引用 lang="less"
即可。
<stylelang="less"></style>复制代码
CSS 命名规范推荐 BEM 命名规范
参考链接:CSS BEM 书写规范[21]
在 src/style
目录下创建 variables.less
全局样式文件:
└── src/ ├── style/ ├── variables.less // 全局样式文件复制代码
在 vite.config.js
配置文件中新增CSS 预处理器相关配置即可实现 less 全局样式:
import{resolve}from'path';exportdefaultdefineConfig({css:{preprocessorOptions:{less:{modifyVars:{hack:`true;@import(reference)"${resolve('src/style/variables.less')}";`,},math:'strict',javascriptEnabled:true,},},},});复制代码
官方文档[22]
在 Vue3 中,改变了以往样式穿透的语法,如果继续使用 ::v-deep
、/deep/
、>>>
等语法的话,会出现一个警告,下面是新的语法:
/*深度选择器*/:deep(selector){/*...*/}/*插槽选择器*/:slotted(selector){/*...*/}/*全局选择器*/:global(selector){/*...*/}复制代码
至此,一个基于 JavaScript + Vite3 + Vue3 + Vue Router + Pinia + Axios + Less 的前端项目开发环境搭建完毕。
项目托管在 GitHub 仓库[23],需要的同学可以去下载下来,参考学习。
接下来增加代码规范约束、提交规范约束、单元测试、自动部署等,让其更完善、更健壮。
随着前端应用逐渐变得大型化和复杂化,在同一个项目中有多个人员参与时,每个人的前端能力程度不等,他们往往会用不同的编码风格和习惯在项目中写代码,长此下去,势必会让项目的健壮性越来越差。解决这些问题,理论上讲,口头约定和代码审查都可以,但是这种方式无法实时反馈,而且沟通成本过高,不够灵活,更关键的是无法把控。不以规矩,不能成方圆,我们不得不在项目使用一些工具来约束代码规范。
本文讲解如何使用 EditorConfig + ESLint + Prettier + Stylelint 组合来实现代码规范化。
这样做带来好处:
-
解决团队之间代码不规范导致的可读性差和可维护性差的问题。
-
解决团队成员不同编辑器导致的编码规范不统一问题。
-
提前发现代码风格问题,给出对应规范提示,及时修复。
-
减少代码审查过程中反反复复的修改过程,节约时间。
-
自动格式化,统一编码风格,从此和脏乱差的代码说再见。
EditorConfig[24] 主要用于统一不同 IDE 编辑器的编码风格。
在项目根目录下添加 .editorconfig
文件:
# 表示是最顶层的 EditorConfig 配置文件root = true# 表示所有文件适用[*]# 缩进风格(tab | space)indent_style = space# 控制换行类型(lf | cr | crlf)end_of_line = lf# 设置文件字符集为 utf-8charset = utf-8# 去除行首的任意空白字符trim_trailing_whitespace = true# 始终在文件末尾插入一个新行insert_final_newline = true# 表示仅 md 文件适用以下规则[*.md]max_line_length = offtrim_trailing_whitespace = false# 表示仅 ts、js、vue、css 文件适用以下规则[*.{ts,js,vue,css}]indent_size = 2复制代码
很多 IDE 中会默认支持此配置,但是也有些不支持,如:VSCode、Atom、Sublime Text 等。
具体列表可以参考官网,如果在 VSCode 中使用需要安装
EditorConfig for VS Code
插件。
EditorConfig for VS Code
ESLint[25] 是针对 EScript 的一款代码检测工具,它可以检测项目中编写不规范的代码,如果写出不符合规范的代码会被警告。
由此我们就可以借助于 ESLint 强大的功能来统一团队的编码规范。
-
`ESLint`[26] - ESLint 本体
-
`eslint-define-config`[27] - 改善 ESLint 规范编写体验
-
`eslint-plugin-vue`[28] - 适用于 Vue 文件的 ESLint 插件
-
`eslint-config-airbnb-base`[29] - Airbnb JavaScript 风格指南
-
`eslint-plugin-import`[30] - 使用
eslint-config-airbnb-base
时必须安装的前置插件 -
`vue-eslint-parser`[31] - 使用
eslint-plugin-vue
时必须安装的 ESLint 解析器npmieslinteslint-define-configeslint-config-airbnb-baseeslint-plugin-importeslint-plugin-vuevue-eslint-parser-D复制代码
Visual Studio Code 编辑器使用 ESLint
配置需要下载插件 ESLint 。
ESLint
JetBrains 系列编辑器(WebStorm、IntelliJ IDEA 等)则不用额外安装插件。
在项目根目录创建 .eslintrc.js
文件,并填入以下内容:
const{defineConfig}=require('eslint-define-config');module.exports=defineConfig({root:true,env:{browser:true,node:true,jest:true,es6:true,},plugins:['vue'],parser:'vue-eslint-parser',parserOptions:{ecmaVersion:'latest',sourceType:'module',allowImportExportEverywhere:true,ecmaFeatures:{jsx:true,},},extends:['airbnb-base','eslint:recommended','plugin:vue/vue3-essential','plugin:vue/vue3-recommended','plugin:prettier/recommended',],rules:{//禁止使用多余的包'import/no-extraneous-dependencies':0,//确保在导入路径内一致使用文件扩展名'import/extensions':0,//确保导入指向可以解析的文件/模块'import/no-unresolved':0,//首选默认导出导入/首选默认导出'import/prefer-default-export':0,//要求使用let或const而不是var'no-var':'error',//禁止使用new以避免产生副作用'no-new':1,//禁止变量声明与外层作用域的变量同名'no-shadow':0,//禁用console'no-console':0,//禁止标识符中有悬空下划线'no-underscore-dangle':0,//禁止在可能与比较操作符相混淆的地方使用箭头函数'no-confusing-arrow':0,//禁用一元操作符++和--'no-plusplus':0,//禁止对function的参数进行重新赋值'no-param-reassign':0,//禁用特定的语法'no-restricted-syntax':0,//禁止在变量定义之前使用它们'no-use-before-define':0,//禁止直接调用Object.prototypes的内置属性'no-prototype-builtins':0,//禁止可以在有更简单的可替代的表达式时使用三元操作符'no-unneeded-ternary':'error',//禁止重复模块导入'no-duplicate-imports':'error',//禁止在对象中使用不必要的计算属性'no-useless-computed-key':'error',//强制使用一致的缩进indent:['error',2],//强制使用骆驼拼写法命名约定camelcase:0,//强制类方法使用this'class-methods-use-this':0,//要求构造函数首字母大写'new-cap':0,//强制一致地使用function声明或表达式'func-style':0,//强制一行的最大长度'max-len':0,//要求return语句要么总是指定返回的值,要么不指定'consistent-return':0,//强制switch要有default分支'default-case':2,//强制剩余和扩展运算符及其表达式之间有空格'rest-spread-spacing':'error',//要求使用const声明那些声明后不再被修改的变量'prefer-const':'error',//强制箭头函数的箭头前后使用一致的空格'arrow-spacing':'error',},overrides:[{files:['*.vue'],rules:{//要求组件名称总是多个单词'vue/multi-word-component-names':0,},},],});复制代码
关于更多配置项信息,请前往 ESLint 官网查看 ESLint-Configuring[32]
在项目根目录添加一个 .eslintignore
文件,内容如下:
distnode_modules!.prettierrc.js复制代码
Prettier[33] 是一款强大的代码格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等语言,基本上前端能用到的文件格式它都可以搞定,是当下最流行的代码格式化工具。
npmiprettier-D复制代码
Visual Studio Code 编辑器使用 Prettier
配置需要下载插件 Prettier - Code formatter 。
Prettier - Code formatter
JetBrains 系列编辑器(WebStorm、IntelliJ IDEA 等)则不用额外安装插件,可直接使用 Prettier
配置。
Prettier 支持多种格式的配置文件[34],比如 .json
、.yml
、.yaml
、.js
等。
在项目根目录创建 .prettierrc.js
文件,并填入以下内容:
module.exports={//一行最多120字符printWidth:120,//使用2个空格缩进tabWidth:2,//不使用缩进符,而使用空格useTabs:false,//行尾需要有分号semi:true,//使用单引号singleQuote:true,//对象的key仅在必要时用引号quoteProps:'as-needed',//jsx不使用单引号,而使用双引号jsxSingleQuote:false,//末尾需要有逗号trailingComma:'all',//大括号内的首尾需要空格bracketSpacing:true,//jsx标签的反尖括号需要换行jsxBracketSameLine:false,//箭头函数,只有一个参数的时候,也需要括号arrowParens:'always',//每个文件格式化的范围是文件的全部内容rangeStart:0,rangeEnd:Infinity,//不需要写文件开头的@prettierrequirePragma:false,//不需要自动在文件开头插入@prettierinsertPragma:false,//使用默认的折行标准proseWrap:'preserve',//根据显示样式决定html要不要折行htmlWhitespaceSensitivity:'css',//vue文件中的script和style内不用缩进vueIndentScriptAndStyle:false,//换行符使用lfendOfLine:'lf',//格式化嵌入的内容embeddedLanguageFormatting:'auto',//html,vue,jsx中每个属性占一行singleAttributePerLine:false,};复制代码
关于更多配置项信息,请前往 Prettier 官网查看 Prettier-Options[35]
在项目根目录添加一个 .prettierignore
文件,内容如下:
##OS.DS_Store.idea.editorconfigpnpm-lock.yaml.npmrc#Ignoredsuffix*.log*.md*.svg*.png*.ico*ignore##Local.husky##Built-files.cachedist复制代码
本项目中的 ESLint 配置使用了 Airbnb JavaScript 风格指南校验,其规则之一是_代码结束后面要加分号_,而在 Prettier 配置文件中加了_代码结束后面不加分号_配置项,从而冲突了。
解决两者冲突问题,需要用到 eslint-plugin-prettier 和 eslint-config-prettier。
-
eslint-plugin-prettier
将 Prettier 的规则设置到 ESLint 的规则中 -
eslint-config-prettier
关闭 ESLint 中与 Prettier 中会发生冲突的规则
最后形成优先级:Prettier 配置规则
> ESLint 配置规则
npmieslint-plugin-prettiereslint-config-prettier-D复制代码
修改 .eslintrc.js
文件,在 extends
中添加 plugin:prettier/recommended
规则(此规则一定要加在最后)。
module.exports={extends:['airbnb-base','eslint:recommended','plugin:vue/vue3-essential','plugin:vue/vue3-recommended','plugin:prettier/recommended'],}复制代码
Visual Studio Code 在 settings.json
设置文件中,增加以下代码:
{"editor.codeActionsOnSave":{"source.fixAll":true,"source.fixAll.eslint":true}}复制代码
WebStorm 打开设置窗口,按如下操作,最后点击 Apply
-> OK
:
WebStorm
Stylelint 是一个强大、先进的 CSS 代码检查器(linter),可以帮助你规避 CSS 代码中的错误并保持一致的编码风格。
-
`Stylelint`[36] - Stylelint 本体
-
`stylelint-config-prettier`[37] - 关闭 Stylelint 中与 Prettier 中会发生冲突的规则
-
`stylelint-config-standard`[38] - Stylelint 官方推荐规则
-
`stylelint-config-recommended-vue`[39] - 检验 vue 文件中的样式
-
`stylelint-order`[40] - CSS 属性顺序规则插件
npmistylelintstylelint-config-prettierstylelint-config-standardstylelint-config-recommended-vuestylelint-order-D复制代码
Visual Studio Code 编辑器使用 Stylelint
配置需要下载插件 Stylelint 。
Stylelint
JetBrains 系列编辑器(WebStorm、IntelliJ IDEA 等)则不用额外安装插件。
在项目根目录创建 .stylelintrc.js
文件,并填入以下内容:
module.exports={root:true,defaultSeverity:'error',extends:['stylelint-config-standard','stylelint-config-prettier'],plugins:['stylelint-order'],rules:{//不允许未知函数'function-no-unknown':null,//指定类选择器的模式'selector-class-pattern':null,//禁止空源码'no-empty-source':null,//指定字符串使用单引号'string-quotes':'single',//禁止未知的@规则'at-rule-no-unknown':[true,{ignoreAtRules:['tailwind','apply','variants','responsive','screen','function','if','each','include','mixin',],},],//指定@规则名的大小写'at-rule-name-case':'lower',//指定缩进indentation:[2,{severity:'warning',},],//禁止未知的伪类选择器'selector-pseudo-class-no-unknown':[true,{ignorePseudoClasses:['global'],},],//禁止未知的伪元素选择器'selector-pseudo-element-no-unknown':[true,{ignorePseudoElements:['v-deep'],},],'order/properties-order':['position','top','right','bottom','left','z-index','display','justify-content','align-items','float','clear','overflow','overflow-x','overflow-y','margin','margin-top','margin-right','margin-bottom','margin-left','padding','padding-top','padding-right','padding-bottom','padding-left','width','min-width','max-width','height','min-height','max-height','font-size','font-family','font-weight','border','border-style','border-width','border-color','border-top','border-top-style','border-top-width','border-top-color','border-right','border-right-style','border-right-width','border-right-color','border-bottom','border-bottom-style','border-bottom-width','border-bottom-color','border-left','border-left-style','border-left-width','border-left-color','border-radius','text-align','text-justify','text-indent','text-overflow','text-decoration','white-space','color','background','background-position','background-repeat','background-size','background-color','background-clip','opacity','filter','list-style','outline','visibility','box-shadow','text-shadow','resize','transition',],},}复制代码
在项目根目录添加一个 .stylelintignore
文件,内容如下:
# .stylelintignore# 旧的不需打包的样式库*.min.css# 其他类型文件*.js*.jpg*.woff# 测试和打包目录/test//dist/*/public/*public/*/node_modules/复制代码
Stylelint
v14 版本默认不支持 vue 文件中的 style 代码自动检测,详情查看官方迁移指南[41]
-
`stylelint-config-html`[42] - 解析 vue 文件
-
`postcss-html`[43] - 使用
stylelint-config-html
依赖的模块 -
`postcss-less`[44] - 对 less 文件进行解析
npmistylelint-config-htmlpostcss-htmlpostcss-less-D复制代码
修改 .stylelintrc.js
文件,添加如下配置:
module.exports={overrides:[{files:['*.vue','**/*.vue','*.html','**/*.html'],extends:['stylelint-config-html'],rules:{//指定关键帧名称的模式'keyframes-name-pattern':null,//禁止未知的伪类选择器'selector-pseudo-class-no-unknown':[true,{ignorePseudoClasses:['deep','global'],},],//禁止未知的伪元素选择器'selector-pseudo-element-no-unknown':[true,{ignorePseudoElements:['v-deep','v-global','v-slotted'],},],},},{files:['*.less','**/*.less'],customSyntax:'postcss-less',extends:['stylelint-config-standard','stylelint-config-recommended-vue'],},],};复制代码
Visual Studio Code 在 settings.json
设置文件中,增加以下代码:
{"stylelint.validate":["css","less","postcss","scss","vue","sass"]}复制代码
在项目中已集成 ESLint 和 Prettier,在编码时,这些工具可以对代码进行实时校验,在一定程度上能有效规范所写代码,但有些人可能觉得这些限制很麻烦,从而选择视“提示”而不见,依旧按自己编程风格来写代码,或者干脆禁用掉这些工具,开发完成就直接把代码提交到了仓库,日积月累,ESLint 也就形同虚设。
所以,还需要做一些限制,让没通过 ESLint 检测和修复的代码禁止提交,从而保证仓库代码都是符合规范的。
为了解决这个问题,需要用到 Git Hook,在本地执行 git commit
的时候,就对所提交的代码进行 ESLint 检测和修复(即执行 eslint \--fix
),如果这些代码没通过 ESLint 规则校验,则禁止提交。
实现这一功能,需要借助 husky[45] + lint-staged[46] 。
注意:本项目使用 husky 6.x 版本,6.x 版本配置方式跟之前版本有较大差异,当发现配置方法不一致时,一切以 husky 官网[47]为准。
使用 husky-init
命令快速在项目初始化 husky
配置:
#初始化仓库gitinit#初始化npxhusky-init#安装依赖npminstall复制代码
husky 包含很多 hook
(钩子),常用有:pre-commit
、commit-msg
。
使用 pre-commit
来触发 ESLint 命令,修改 .husky/pre-commit
文件触发命令:
eslint--fix./src--ext.vue,.js,.ts复制代码
pre-commit
hook 文件作用是:当执行 git commit \-m "xxx"
时,会先对 src
目录下所有的 .vue
、.js
、.ts
文件执行 eslint \--fix
命令,如果 ESLint 通过,成功 commit
,否则终止 commit
。
但是又存在一个问题:有时候明明只改动了一两个文件,却要对所有的文件执行 eslint \--fix
。
假如这是一个历史项目,在中途配置了 ESLint 规则,那么在提交代码时,也会对其他未修改的“历史”文件都进行检查,可能会造成大量文件出现 ESLint 错误,显然这不是我们想要的结果。
所以只需要用 ESLint 修复此次写的代码,而不去影响其他的代码,此时需要借助 lint-staged 工具。
lint-staged 一般结合 husky 来使用,它可以让 husky 的 hook
触发的命令只作用于 git
暂存区的文件,而不会影响到其他文件。
npmilint-staged-D复制代码
在 package.json
里增加 lint-staged
配置项:
{"lint-staged":{"*.{js,jsx,ts,tsx}":["prettier--write","eslint--fix"],"*.vue":["prettier--write","eslint--fix","stylelint--fix"],"*.{html,vue,vss,sass,less}":["prettier--write","stylelint--fix"],"package.json":["prettier--write"],"*.md":["prettier--write"]},}复制代码
修改 .husky/pre-commit
文件触发命令为:
npxlint-staged复制代码
pre-commit
经过以上配置之后,就可以在每次提交之前对所有代码进行格式化,保证线上代码的规范性。
多人协作项目中,在提交代码环节,也存在一种情况:不能保证每个人对提交信息的准确描述,因此会出现提交信息紊乱、风格不一致的情况。
如果 git commit
的描述信息精准,在后期维护和 Bug 处理时会变得有据可查,项目开发周期内还可以根据规范的提交信息快速生成开发日志,从而方便我们追踪项目和把控进度。
社区最流行、最知名、最受认可的 Angular[48] 团队提交规范:
Angular 团队提交规范
参考链接: Angular Style Commit Message Conventions[49]
commit message
由 Header、Body、Footer 组成。
<Header><Body><Footer>复制代码
Header 部分包括三个字段 type(必需)、scope(可选)和 subject(必需)。
<type>(<scope>):<subject>复制代码
type
type 用于说明 commit 的提交类型(必须是以下几种之一)。
值
描述
feat
新增功能
fix
修复问题
docs
文档变更
style
代码格式(不影响功能,例如空格、分号等格式修正)
refactor
代码重构
perf
改善性能
test
测试
build
变更项目构建或外部依赖(例如 scopes: webpack、gulp、npm 等)
ci
更改持续集成软件的配置文件和 package 中的 scripts 命令,例如 scopes: Travis, Circle 等
chore
变更构建流程或辅助工具
revert
代码回退
scope
scope 用于指定本次 commit 影响的范围。
scope 依据项目而定,例如在业务项目中可以依据菜单或者功能模块划分,如果是组件库开发,则可以依据组件划分。
subject
subject 是本次 commit 的简洁描述,长度约定在 50 个字符以内,通常遵循以下几个规范:
-
用动词开头,第一人称现在时表述,例如:change 代替 changed 或 changes
-
第一个字母小写
-
结尾不加句号(.)
body 是对本次 commit 的详细描述,可以分成多行。
跟 subject 类似,用动词开头,body 应该说明修改的原因和更改前后的行为对比。
如果本次提交的代码是突破性的变更或关闭缺陷,则 Footer 必需,否则可以省略。
-
突破性的变更
当前代码与上一个版本有突破性改变,则 Footer 以 BREAKING CHANGE 开头,后面是对变动的描述、以及变动的理由。
-
关闭缺陷
如果当前提交是针对特定的 issue,那么可以在 Footer 部分填写需要关闭的单个 issue 或一系列 issues。
-
feat
feat(browser): onUrlChange event (popstate/hashchange/polling)Added new event to browser:- forward popstate event if available- forward hashchange event if popstate not available- do polling when neither popstate nor hashchange availableBreaks $browser.onHashChange, which was removed (use onUrlChange instead)复制代码
-
fix
fix(compile): couple of unit tests for IE9Older IEs serialize html uppercased, but IE9 does not...Would be better to expect case insensitive, unfortunately jasmine doesnot allow to user regexps for throw expectations.Closes #392Breaks foo.bar api, foo.baz should be used instead复制代码
-
style
style(location): add couple of missing semi colons复制代码
-
chore
chore(release): v3.4.2复制代码
一款工程性更强,轻量级,高度自定义,标准输出格式的 commitizen[50] 适配器
官方网站:cz-git[51]
cz-git
npminstall-Dcz-git复制代码
修改 package.json
文件,添加 config
指定使用的适配器
{"scripts":{},"config":{"commitizen":{"path":"node_modules/cz-git"}}}复制代码
cz-git 与 commitlint[52] 进行联动给予校验信息,所以可以编写于 commitlint[53] 配置文件之中。
例如:(⇒ 配置模板[54])
/**@type{import('cz-git').UserConfig}*/module.exports={extends:['@commitlint/config-conventional'],rules:{},prompt:{useEmoji:false,emojiAlign:'center',allowCustomIssuePrefixs:false,allowEmptyIssuePrefixs:false,},};复制代码
本项目配置文件可参考:commitlint.config.js[55]
全局安装的好处在于:在任何项目下都可以利用
cz
或git cz
命令启动命令行工具,生成标准化 commit message
npminstall-gcz-gitcommitizen复制代码
echo'{"path":"cz-git"}'>~/.czrc复制代码
方式一: 编辑 ~/.czrc
文件以 json 形式添加配置,例如:
{"path":"cz-git","useEmoji":true}复制代码
方式二:与 commitlint[56] 配合,在 $HOME
路径下创建配置文件 (↓ 配置模板[57])
在“代码规范”章节中提到,尽管制定了规范,但在多人协作的项目中,总有些人依旧我行我素。
因此提交代码这个环节,也增加一个限制:只让符合 Angular 规范的 commit message 通过。
此功能需借助 @commitlint/config-conventional
和 @commitlint/cli
工具来实现。
-
`@commitlint/cli`[58] - Commitlint 本体
-
`@commitlint/config-conventional`[59] - 通用提交规范
npmi@commitlint/cli@commitlint/config-conventional-D复制代码
在项目根目录创建 commitlint.config.js
文件,并填入以下内容:
module.exports={extends:['@commitlint/config-conventional']}复制代码
使用 husky 命令在 .husky
目录下创建 commit-msg
文件,并在此执行验证命令:
npxhuskyadd.husky/commit-msg"npx--no-installcommitlint--edit$1"复制代码
commit-msg
本项目完整代码托管在 GitHub 仓库[60],欢迎点亮小星星 🌟🌟
本章节将介绍如何使用 CI(Continuous Integration 持续集成)服务来完成项目部署工作。
常见的 CI 工具有 GitHub Actions、GitLab CI、Travis CI、Circle CI 等。
本项目使用 GitHub Actions
来完成这一操作。
🔗 参考链接:GitHub Actions 入门教程[61]
因为 GitHub Actions 只对 GitHub 仓库有效,所以创建 GitHub 仓库[62]来托管项目代码。
创建 GitHub 仓库
-
master
分支存储项目源代码 -
gh-pages
分支存储打包后的静态文件
创建一个有 repo 和 workflow 权限的 GitHub Token[63]
创建 GitHub Token
注意:新生成的 Token 只会显示一次。
Token
将上述创建的 Token 添加到 GitHub 仓库中的 Secrets
里,并将这个新增的 secret
命名为 VITE_VUE_DEPLOY
。
步骤:仓库 -> Settings
-> Secrets
-> Actions
-> New repository secret
。
New secret
注意:新创建的 secret
VITE_VUE_DEPLOY
在 Actions 配置文件中要用到,两个地方需保持一致!
打开 package.json
文件,新增 homepage
字段,表示该应用发布后的根目录(参见官方文档[64])。
"homepage":"https://[username].github.io/github-actions-demo",复制代码
上面代码中,将 [username]
替换成你的 GitHub 用户名,参见范例[65]。
(1)在项目根目录下创建 .github
目录。
(2)在 .github
目录下创建 workflows
目录。
(3)在 workflows
目录下创建 deploy.yml
文件。
.github/workflows/deploy.yml
name:ViteVueDeployon:push:#master分支有push时触发branches:[master]jobs:deploy:#指定虚拟机环境runs-on:ubuntu-lateststrategy:matrix:node-version:[14.x,16.x]steps:-name:Checkout#拉取GitHub仓库代码uses:actions/checkout@v3-name:UseNode.js${{matrix.node-version}}#设定Node.js环境uses:actions/setup-node@v3with:node-version:${{matrix.node-version}}-name:Install#安装依赖run:npminstall-name:Build#打包run:npmrunbuild-name:Deployuses:JamesIves/github-pages-deploy-action@v4with:#部署打包目录folder:dist#密钥名token:${{secrets.VITE_VUE_DEPLOY}}#分支branch:gh-pages复制代码
🔗 通过此链接 ElanYoung.github.io/vite-vue-js…[66] 即可访问本项目
本文从技术选项到架构搭建、从代码规范约束到提交信息规范约束,一步一步带领大家如何从一个最简单的前端项目骨架到规范的前端工程化环境,基本涵盖前端项目开发的整个流程,特别适合刚接触前端工程化的同学学习。
因篇幅较长,所涉及技术点较多,难免会出现错误,希望大家多多指正,谢谢大家!
关于本文-
作者:威廉王子
https://juejin.cn/post/7156957907890733063
往期推荐
最后
-
欢迎加我微信,拉你进技术群,长期交流学习...-
-
欢迎关注「前端Q」,认真学前端,做个专业的技术人...
前端Q
我是winty,专注分享前端知识和各类前端资源,乐于分享各种有趣的事,关注我,一起做个有趣的人~
118篇原创内容
公众号
,
点个在看支持我吧