你在寻找Vue3移动端项目框架嘛?请看这里
现在web开发变得更加美妙高效,在于开发工具设计得更好了,丰富性与易用性,都有所提高。丰富性带来了一个幸福的烦恼,就是针对实际应用场景,如何选择工具 ?
1. Vue Cli和Vite之间的选择
Vite的开发环境体验好,基于浏览器原生ES6 Modules提供的功能,不对ES高版本语法进行转译,省略掉耗时的打包流程, 可是考虑到:
1) 项目要用到真机调试功能,开发环境下调试代码时不能使用ES高版本的语法,用着不顺畅。
后面发现可用@vitejs/plugin-legacy解决此问题。
import legacy from '@vitejs/plugin-legacy';
2) 受制于历史项目包袱,感受到Vite的一些痛点:
- Vite最新版2.7.x版本自带的less-loader, 将背景色的rgba属性转换成四位16进制在有些手机上存在兼容性问题。
- 与某些第三方工具库(比如说Cache-Router)不兼容,编译会报错。
- Vite的缓存机制,有时候让人有些困惑,代码修改了,重启之后都不生效,要手动删除node_modules下的.vite文件夹才生效。
- 给命令行动态添加自定义参数不太方便。
- Vite不支持装饰器语法,而有的第三方库用到了装饰器语法
3) Vite脚手架默认不集成TypeScript,Vue-Router,Vuex功能,使用起来不太方便
4) Vue Cli作为久经考验的成熟构建工具,稳定坑少,使用者众多,在生态环境和插件数量方面更好。
所以最终选择vue-cli最为Vue项目的脚手架。若是新项目,个人还是比较推荐使用Vite,构建速度确实快。
安装最新版本vue脚手架
npm install -g @vue/cli@next
安装成功后通过查看版本命令,确认是否安装成功
vue -V @vue/cli 5.0.0-rc.1
2 创建vue3项目
vue create vue3-demo
Vue CLI v5.0.0-rc.1
? Please pick a preset:
Default ([Vue 2] babel, eslint)
Default (Vue 3) ([Vue 3] babel, eslint) // 不选择默认的vue3配置,是因为没有vue-router+typescript功能,需要自己引入
> Manually select features // 手动选择特性
Vue CLI v5.0.0-rc.1 ? Please pick a preset: Manually select features ? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed) >(*) Choose Vue version (*) Babel // 添加babel (*) TypeScript // 添加类型约束功能 ( ) Progressive Web App (PWA) Support (*) Router // 添加路由功能 (*) Vuex // 添加状态管理功能 (*) CSS Pre-processors // 添加样式预编译功能 (*) Linter / Formatter // 添加代码质量校验提示和格式化功能 ( ) Unit Testing ( ) E2E Testing
Vue CLI v5.0.0-rc.1 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter ? Choose a version of Vue.js that you want to start the project with 2.x > 3.x // 选择vue3
Vue CLI v5.0.0-rc.1 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter ? Choose a version of Vue.js that you want to start the project with 3.x ? Use class-style component syntax? (y/N) N // 是否使用class组件语法, ,选N 项目中用Composition API
为了在Vue中使用TypeScript中,许多开发者选择了Class-Style Component 解决方案,时至今日,还有另外一个方案,Composition API 撰写的代码会完美享用类型推导,并且也不用做太多额外的类型标注。这也同样意味着你写出的 JavaScript 代码几乎就是 TypeScript 的代码。即使是非 TypeScript 开发者也会因此得到更好的 IDE 类型支持而获益。
常规风格
export default { data(){ return { selectOptions: ['A1', 'A2'], results: [], // ... } } }
class 组件风格
import { Vue, Component } from 'vue-property-decorator' @Component export default class Game extends Vue { // 定义data private selectOptions = ['A1', 'A2'] private results: string[] = [] ... }
? Use class-style component syntax? Yes ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes // 是否使用babel工具自动为转换后的 TypeScript 代码注入 polyfiills,此处选择 Y ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes // 使用history网址路径风格,hash路径有点丑陋 ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Less // sass在国内安装经常出错,所以选less ? Pick a linter / formatter config: Prettier // 校验配置选择 eslint+prettier组合,沿袭项目使用习惯 ? Pick additional lint features: Lint on save // 保存代码时校验代码质量, 提交代码时才去校验代码质量有些滞后 ? Where do you prefer placing config for Babel, ESLint, etc. In dedicated config files // 将Babel,ESlint等配置文件从package.json中独立出来,因为json文件不能写注释 ? Save this as a preset for future projects? Y // 将此配置保存起来,用于将来的项目 ,下次用vue-cli生成项目时,可以看到之前保存的配置项
启动服务
cd vue3-demo && yarn serve
在VSCode中打开app.vue发现文件中有许多红色的告警波浪线,安装Volar扩展,,绝大多数语法报错消失。顺便说一下为什么要使用Volar扩展,Vetur对<script setup>里面定义的响应式变量支持度不够好。
<script setup> // ... // 仅用于template,未在script中被使用,会报 count is declared but its value is never read.Vetur(6133) const count = ref(1) //... </script>
3 配置vue文件保存时自动格式化
代码美化功能,是一个重要的影响开发体验的指标。书写潦草的代码,按下Ctrl+S保存之后,瞬间变成整洁有序, 这种视觉感受,让人神清气爽。
在项目下新建.vscode/settings.json,内容如下:
{ // ... "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "[vue]": { "editor.defaultFormatter": "johnsoncodehk.volar" }, "[json]": { "editor.defaultFormatter": "vscode.json-language-features" }, // ... }
修改项目下的.prettierrc文件为.prettierrc.js, 内容如下:
module.exports = { // 1.一行代码的最大字符数,默认是80(printWidth: <int>) printWidth: 120, // 2.tab宽度为2空格(tabWidth: <int>) tabWidth: 2, // 3.是否使用tab来缩进,我们使用空格(useTabs: <bool>) useTabs: false, // 4.结尾是否添加分号 semi: true, // 5.使用单引号(singleQuote: <bool>) singleQuote: true, // 6.object对象中key值是否加引号(quoteProps: "<as-needed|consistent|preserve>")as-needed只有在需求要的情况下加引号,consistent是有一个需要引号就统一加,preserve是保留用户输入的引号 quoteProps: 'as-needed', // 7.在jsx文件中的引号需要单独设置(jsxSingleQuote: <bool>) jsxSingleQuote: false, // 8.尾部逗号设置,es5是尾部逗号兼容es5,none就是没有尾部逗号,all是指所有可能的情况,需要node8和es2017以上的环境。(trailingComma: "<es5|none|all>") trailingComma: 'all', // 9.object对象里面的key和value值和括号间的空格(bracketSpacing: <bool>) bracketSpacing: true,// 10.箭头函数单个参数的情况是否省略括号,默认always是总是带括号(arrowParens: "<always|avoid>") arrowParens: 'always', // 11.range是format执行的范围,可以选执行一个文件的一部分,默认的设置是整个文件(rangeStart: <int> rangeEnd: <int>) rangeStart: 0, rangeEnd: Infinity, // 12. requirePragma: <bool>,格式化有特定开头编译指示的文件 比如下面两种 /** * @prettier */ // or /** * @format */ requirePragma: false, // 13.insertPragma: <bool> 自动插入pragma到已经完成的format的文件开头 insertPragma: false, // 14. proseWrap: "<always|never|preserve>" 文章换行,默认情况下会对你的markdown文件换行 进行format会控制在printwidth以内 proseWrap: 'always', // 15. htmlWhitespaceSensitivity: "<css|strict|ignore>" html中的空格敏感性 // html文档片段 1<b>2</b>3 原本显示为123, 不设置忽略空格的话格式化后会变成 1<b> 2 </b>3 显示为1 2 3 htmlWhitespaceSensitivity: 'ignore', // 16. vue script和style标签中是否缩进,开启可能会破坏编辑器的代码折叠 vueIndentScriptAndStyle: false, // 17. endOfLine: "<lf|crlf|cr|auto>" 行尾换行符,默认是lf, endOfLine: 'lf', // 18. 控制被引号包裹的代码是否进行格式化, 默认是auto, embeddedLanguageFormatting: 'off' }
4 配置移动端UI库
做移动端开发,虽然定制性比较强。绝大多数UI库组件都用不到,但像Toast,Modal,Picker,Form,PullRefresh等组件几乎是必用,所以需要引入移动端UI库。
vue3移动端UI库的选择:
antd-mobile-vue-next | 移入项目之后,编译报错 |
Vux | UI风格是绿色系,与现有项目使用的蓝色系风格UI不符, 所以没用 |
Vant | 是业界主流的移动端组件库之一,UI色系风格与历史项目相符,支持vue3,组件功能优于Vux,已Toast为例,Vant提供了网络加载的Toast, Vux未提供。Vant总共提供了69个(不含组合api)涵盖基础,表单,反馈,展示,导航,业务六大类组件。 |
yarn add vant@3
按需引入
按需加载需要借助babel-plugin-import
, 这样就可以只引入需要的组件,以减小项目体积
yarn add babel-plugin-import -D
对babel.config.js进行配置
module.exports = { presets: ["@vue/cli-plugin-babel/preset"], plugins: [ [ "import", { libraryName: "vant", libraryDirectory: "es", style: true, }, "vant", ], ], };
main.js中引入vant的样式
import { createApp } from "vue"; import App from "./App.vue"; import "vant/lib/index.css"; createApp(App).mount("#app");
在App.vue中引入组件
<template> <div> <Button type="primary">主要按钮</Button> <img alt="Vue logo" src="./assets/logo.png" /> <HelloWorld msg="Welcome to Your Vue.js App" /> </div> </template> <script> import HelloWorld from "./components/HelloWorld.vue"; import { Button } from "vant"; export default { name: "App", components: { HelloWorld, Button, }, }; </script>
5 配置开发环境请求代理
请求代理是解决本地开发请求跨域的最佳方式之一,对前后端代码都没有侵入性。 在vue.config.js中添加代理转发配置:
const { defineConfig } = require("@vue/cli-service"); module.exports = defineConfig({ transpileDependencies: true, devServer: { port: 9000, host: "localhost", https: false, open: "/", proxy: { "/api": { target: "http://192.168.xx.xx:50000", secure: false, changeOrigin: true, logLevel: "debug", }, }, }, // .... });
6 配置路径别名
项目统一使用路径别名,好处是在代码中不用写../../../
之类让人看着有些云里雾里的文件引用路径,此外用短路径替代长路径,书写也更方便。而且编辑器对于别名路径也有提示,可以跳转。
需要注意的是,如果使用了TypeScript,除了要在vue.config.js中配置路径别名之外,还需要在tsconfig.json配置路径别名,否则虽然打包编译不报错,但是代码编辑器却会提示引用路径有错误。
vue.config.js路径别名配置
const path = require('path'); module.exports = { configureWebpack: { resolve: { alias: { '@': path.join(__dirname, 'src/') } } } }
tsconfig.json路径别名配置
{ "compilerOptions": { // ... "baseUrl": ".", "paths": { "@/*": ["src/*"], } }, "include": [ "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", // 包含自定义声明文件 "typings/**/*.d.ts", ], // ... }
7 设置自定义环境变量
开发调试的时候,需要动态在命令行添加动态参数,用以执行开发环境的调试逻辑,将自定义的命令行参数传到业务文件的方法是:
在package.json中传入自定义环境变量
{ // ... "scripts": { "start:wx": "ytt && vue-cli-service serve --mode local --wx true", // ... }, }
在vue.config.js中设置自定义环境变量
// ... const webpack = require('webpack'); const { defineConfig } = require('@vue/cli-service'); const { wx, mode } = require('minimist')(process.argv.slice(2)); console.log({ wx, mode }); // console.log(process.env.VITE_API_HOST); module.exports = defineConfig({ // ... configureWebpack: { plugins: [ // 定义环境变量 new webpack.DefinePlugin({ 'process.env.WX_JS_SDK_ENABLED': wx, // 是否真机调试SDK模式 'process.env.CURRENT_ENV': JSON.stringify(mode), }), ], }, });
在业务文件中接收自定义环境变量
import { createApp } from 'vue'; import 'vant/lib/index.css'; import App from './App.vue'; import router from './router'; import store from './store'; console.log(process.env.WX_JS_SDK_ENABLED, process.env.CURRENT_ENV); createApp(App).use(store).use(router).mount('#app');
8 配置stylelint校验规则
stylelint的好处:
- 可以发现样式书写问题,如无效的十六进制值;重复的选择器;未命名的动画名称;错误的线性渐变语法;后面的属性覆盖前面的属性;前面的选择器优先级更高,覆盖后面的属性;选择器下未设置任何属性等。
- 能使所有人写的样式风格都一致,按照业内知名公司 (GitHub、Google、Airbnb)的样式规范要求写样式。
- 保存时自动对样式书写进行优化(如采用样式的简写形式,删除无意义小数点前面的0,调整样式顺序等),修复可以修复的样式错误。
1.安装依赖
yarn add -D stylelint stylelint-config-recess-order stylelint-order stylelint-config-standard stylelint-less stylelint-webpack-plugin postcss-html
2. 创建.stylelintrc.js 样式校验规则
module.exports = { plugins: ['stylelint-less'], extends: ['stylelint-order', 'stylelint-config-standard', 'stylelint-config-recess-order'], rules: { indentation: 2, 'at-rule-no-unknown': [true, { ignoreAtRules: ['mixin', 'extend', 'content', 'include'] }], 'no-empty-source': null, // null是关闭规则的意思--less文件内容可以为空 'no-descending-specificity': null, //禁止特异性较低的选择器在特异性较高的选择器之后重写 'font-family-no-missing-generic-family-keyword': null, // 关闭必须设置通用字体的规则 'property-no-unknown': [ true, { ignoreProperties: ['box-flex'], // 忽略某些未知属性的检测 }, ], 'selector-pseudo-element-no-unknown': [ true, { ignorePseudoElements: ['ng-deep'], // 忽略ng-deep这种合法的伪元素选择器报警 }, ], 'declaration-colon-newline-after': null, //一个属性过长的话可以写成多行 'media-feature-name-no-unknown': null, // 关闭禁止未知的媒体功能名 // 下面的排序规则是stylelint-config-recess-order的css排序规则, // 要对某个属性排序进行调整,这个属性之前的样式排序都要配置在自定义属性排序中 'order/properties-order': [ { // Must be first. properties: ['all'], }, { // Position. properties: ['position', 'top', 'right', 'bottom', 'left', 'z-index'], }, { // Display mode. properties: ['box-sizing', 'display'], }, { // Flexible boxes. properties: ['flex', 'flex-basis', 'flex-direction', 'flex-flow', 'flex-grow', 'flex-shrink', 'flex-wrap'], }, { // Grid layout. properties: [ 'grid', 'grid-area', 'grid-template', 'grid-template-areas', 'grid-template-rows', 'grid-template-columns', 'grid-row', 'grid-row-start', 'grid-row-end', 'grid-column', 'grid-column-start', 'grid-column-end', 'grid-auto-rows', 'grid-auto-columns', 'grid-auto-flow', 'grid-gap', 'grid-row-gap', 'grid-column-gap', ], }, { // Align. properties: ['align-content', 'align-items', 'align-self'], }, { // Justify. properties: ['justify-content', 'justify-items', 'justify-self'], }, { // Order. properties: ['order'], }, { // Box model. properties: [ 'float', 'width', 'min-width', 'max-width', 'height', 'line-height', 'min-height', 'max-height', 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'overflow', 'overflow-x', 'overflow-y', '-webkit-overflow-scrolling', '-ms-overflow-x', '-ms-overflow-y', '-ms-overflow-style', 'clip', 'clear', ], }, { // Typography. properties: [ 'font', 'font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'font-size-adjust', 'font-stretch', 'font-effect', 'font-emphasize', 'font-emphasize-position', 'font-emphasize-style', '-webkit-font-smoothing', '-moz-osx-font-smoothing', 'font-smooth', 'hyphens', 'color', 'text-align', 'text-align-last', 'text-emphasis', 'text-emphasis-color', 'text-emphasis-style', 'text-emphasis-position', 'text-decoration', 'text-indent', 'text-justify', 'text-outline', '-ms-text-overflow', 'text-overflow', 'text-overflow-ellipsis', 'text-overflow-mode', 'text-shadow', 'text-transform', 'text-wrap', '-webkit-text-size-adjust', '-ms-text-size-adjust', 'letter-spacing', 'word-break', 'word-spacing', 'word-wrap', // Legacy name for `overflow-wrap` 'overflow-wrap', 'tab-size', 'white-space', 'vertical-align', 'list-style', 'list-style-position', 'list-style-type', 'list-style-image', ], }, { // Accessibility & Interactions. properties: [ 'pointer-events', '-ms-touch-action', 'touch-action', 'cursor', 'visibility', 'zoom', 'table-layout', 'empty-cells', 'caption-side', 'border-spacing', 'border-collapse', 'content', 'quotes', 'counter-reset', 'counter-increment', 'resize', 'user-select', 'nav-index', 'nav-up', 'nav-right', 'nav-down', 'nav-left', ], }, { // Background & Borders. properties: [ 'background', 'background-color', 'background-image', "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", 'filter:progid:DXImageTransform.Microsoft.gradient', 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader', 'filter', 'background-repeat', 'background-attachment', 'background-position', 'background-position-x', 'background-position-y', 'background-clip', 'background-origin', 'background-size', 'background-blend-mode', 'isolation', 'border', 'border-color', 'border-style', 'border-width', 'border-top', 'border-top-color', 'border-top-style', 'border-top-width', 'border-right', 'border-right-color', 'border-right-style', 'border-right-width', 'border-bottom', 'border-bottom-color', 'border-bottom-style', 'border-bottom-width', 'border-left', 'border-left-color', 'border-left-style', 'border-left-width', 'border-radius', 'border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius', 'border-image', 'border-image-source', 'border-image-slice', 'border-image-width', 'border-image-outset', 'border-image-repeat', 'outline', 'outline-width', 'outline-style', 'outline-color', 'outline-offset', 'box-shadow', 'mix-blend-mode', 'filter:progid:DXImageTransform.Microsoft.Alpha(Opacity', "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", 'opacity', '-ms-interpolation-mode', ], }, { // SVG Presentation Attributes. properties: [ 'alignment-baseline', 'baseline-shift', 'dominant-baseline', 'text-anchor', 'word-spacing', 'writing-mode', 'fill', 'fill-opacity', 'fill-rule', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'flood-color', 'flood-opacity', 'image-rendering', 'lighting-color', 'marker-start', 'marker-mid', 'marker-end', 'mask', 'shape-rendering', 'stop-color', 'stop-opacity', ], }, { // Transitions & Animation. properties: [ 'transition', 'transition-delay', 'transition-timing-function', 'transition-duration', 'transition-property', 'transform', 'transform-origin', 'animation', 'animation-name', 'animation-duration', 'animation-play-state', 'animation-timing-function', 'animation-delay', 'animation-iteration-count', 'animation-direction', ], }, ], }, };
编译时自动修复
// ... const { defineConfig } = require('@vue/cli-service'); const StylelintPlugin = require('stylelint-webpack-plugin'); module.exports = defineConfig({ // ... configureWebpack: { plugins: [ // ... new StylelintPlugin({ files: ['src/**/*.vue'], // 编译时自动修复 fix: true, // 这样配才能修复vue文件style片段中的less语法 customSyntax: 'postcss-html', }), ], }, });
安装VSCode stylelint扩展
在.vscode/settings.json中配置保存代码时自动修复样式错误
{ "editor.codeActionsOnSave": { // ... "source.fixAll.stylelint": true }, // 关闭vscode自带的css,less,scss报错提示 "css.validate": false, "less.validate": false, "scss.validate": false, "stylelint.validate": ["css", "less"] }
9 配置yapi-to-typescript
typescript面世以来,经历了一个从厌烦到喜欢的过程,厌恶的是要在原来开发的基础上,多做一些繁杂数据类型定义的工作,喜欢的是确实帮助开发者发现了许多语法错误,享受到接口和传参的便利提示, 综合起来,还是利大于弊,所以 越来越多的开发者开始加入ts大家庭。yapi-to-typescript这个工具,可以帮我们省去接口数据类型定义的工作量。让我们更愉快的与ts玩耍。
安装依赖
yarn add -D yapi-to-typescript
创建ytt.config.ts配置文件
import { defineConfig } from 'yapi-to-typescript'; /** * 生成Api接口名称 Interface和ChangeCase数据类型参见node_modules\yapi-to-typescript\lib\esm\index.d.ts定义 * @param interfaceInfo : Interface * @param changeCase:ChangeCase * @returns 请求响应接口名称--pascal命名 */ function genApiInterfaceName(interfaceInfo, changeCase) { // 取解析路径dir最尾部的路径作为前缀路径 const lastPath = interfaceInfo.parsedPath.dir.split('/').pop(); // 拼接前缀路径+文件名称 return `${changeCase.pascalCase(lastPath)}${changeCase.pascalCase(interfaceInfo.parsedPath.name)}`; } export default defineConfig([ { serverUrl: 'https://yapi.xxx.com', typesOnly: true, target: 'typescript', reactHooks: { enabled: false, }, prodEnvName: '项目名称', // 将生成文件路径转化成小驼峰命名方式 outputFilePath: (interfaceInfo, changeCase) => { // 文件夹名称取api-url路径末尾2个 const filePathArr = interfaceInfo.path.split('/').slice(-2); const filePath = filePathArr.map((item) => changeCase.camelCase(item)).join('/'); return `typings/httpTypes/${filePath}.ts`; }, // 生成ts文件中请求参数interface名称,将下划线命名转换成pascal命名 getRequestDataTypeName: (interfaceInfo, changeCase) => { return `${genApiInterfaceName(interfaceInfo, changeCase)}Request`; }, // 生成ts文件中请求响应数据interface名称,将下划线命名转换成pascal命名 getResponseDataTypeName: (interfaceInfo, changeCase) => { return `${genApiInterfaceName(interfaceInfo, changeCase)}Response`; }, // 响应数据中要生成ts数据类型的键名 dataKey: 'retdata', projects: [ { // token获取方式: 在yapi-设置-token配置中查看 token: 'xxx', // 分类id查找方式: 点击接口左侧的分类菜单,查看url地址栏最后面的数字获取 // 分类id配置特别重要,配置错了无法生成对应的ts数据类型定义文件 categories: [ { id: [xxx], // 民生立减金API分类id }, ], }, ], }, ]);
3.在package.json中配置ytt指令
{ // ... "scripts": { "start": "ytt && vue-cli-service serve --mode local", "build": "ytt && vue-cli-service build --mode local", "ytt": "ytt", }, }
4.在.gitignore中添加对httpTypes下文件的忽略
# ... /typings/httpTypes/* # ...
10 配置commitlint
commit message 是程序员开发的日常高频操作,自然状态下commit message 呈现五花八门的书写风格,不利于阅读和维护,规范的 commit message 有助于团队做 code review, 输出清晰明了的 CHANGELOG, 有利于项目的长期维护。
安装依赖包
yarn add -D husky conventional-changelog-cli @commitlint/{cli,config-conventional}
创建提交校验配置文件commitlint.config.js
/** * git commit最佳实践 * 1.经常commit,One Thing,One Commit * 2.commit之前测试,不要commit一半工作; * 3.编写规范的commit message */ /** * Commit message 包括三个部分:Header,Body 和 Footer * <type>(<scope>): <subject> * 空一行 * <body> * 空一行 * <footer> */ module.exports = { extends: ['@commitlint/config-conventional'], rules: { // Header包括三个字段:type(必需)、scope(可选)和subject(必需)。最多200字 'header-max-length': [2, 'always', 200], // 提交类型<type>枚举 'type-enum': [ 2, 'always', [ 'init', // 项目初始化 'clean', // 清理过时无用文件 'merge', // 合并代码 'style', // 修改样式文件(包括css/less/sass,图片,字体文件) 'format', // 格式化,不影响代码含义的修改,比如空格、格式缩进、缺失的分号等 'build', // 改变构建流程、或者增加依赖库、工具等 如webpack.config.js,package.json yarn.lock 'chore', // 各种配置文件的修改, 如.gitignore,tsconfig.json,.vscode,.tenone, eslint/stylelint,envConfig 'ci', // 对CI配置文件或脚本进行了修改 'docs', // 修改项目说明文档 'feat', // 新增功能 'fix', // 修复bug 'perf', // 性能优化 'refactor', // 既不是修复bug也不是添加功能的代码重构 'revert', // 版本回退 'test', // 修改测试用例 ], ], // 格式-可选值 // 'lower-case' 小写 lowercase // 'upper-case' 大写 UPPERCASE // 'camel-case' 小驼峰 camelCase // 'kebab-case' 短横线 kebab-case // 'pascal-case' 大驼峰 PascalCase // 'sentence-case' 首字母大写 Sentence case // 'snake-case' 下划线 snake_case // 'start-case' 所有首字母大写 start-case // <type> 不能为空 'type-empty': [2, 'never'], // <type> 格式 小写 'type-case': [2, 'always', 'lower-case'], // <scope> 关闭改动范围不能为空规则 'scope-empty': [0, 'never'], // <scope> 格式 小写 'scope-case': [2, 'always', 'lower-case'], // <subject> 不能为空 'subject-empty': [2, 'never'], // <subject> 关闭 以.为结束标志 'subject-full-stop': [0, 'never', '.'], // <subject> 格式 'subject-case': [2, 'never', []], // <body> 以空行开头 'body-leading-blank': [1, 'always'], // <footer> 以空行开头 'footer-leading-blank': [1, 'always'], }, };
3. 创建提交校验shell脚本 husky.sh和commit-msg
yarn husky install
在.husky文件夹下创建commit-msg文件
npx husky add .husky/commit-msg
在.husky/commit-msg文件中写入
#!/bin/sh . "$(dirname "$0")/_/husky.sh" # 提交记录检查 yarn commitlint --edit $1 # 代码重复率检测 yarn jscpd # 格式化检查 yarn format:check # eslint检查 yarn lint:check
11 配置代码重复率检测工具jscpd
代码的简洁之道有一条铁律是 Don't Repeat Yourself,那么如何快速地检测出项目出是否存在着大段的重复代码,靠人工检查显然不可取,这种重复体力活应该交给工具。
检测前端代码重复率的工具有jsinspect、jscpd,PMD-CPD(PMD's Copy/Paste Detector)
- jsinspect工具支持js和jsx格式的文件,基于抽象语法树,可以检测出结构类似的代码块
- PMD-CPD工具支持js文件检测,也可以自己开发扩展包来解析指定的语言
- jscpd工具支持文件格式广泛,如js、jsx、vue、ts、less,java、oc等。其重复率判定依据为一定长度标识符的MD5值是否相同
每个工具各有其优缺点,若只需要检测js或jsx文件,且对检测结果要求较高,可以选择jsinspect或者PMD-CPD工具,若考虑检测工具的通用性,可以选择jscpd工具。
npm install -g jscpd
jscpd src/*
{
// 重复率阈值 "threshold": 0.1,
// 报告输出格式 "reporters": [ "html", "console" ], "ignore": [ "dist/**", "node_modules/**" ],
// 文件路径使用相对路径 "absolute": false }
--min-tokens | -k :代码的最小块大小。小于的代码块min-tokens将被跳过,默认为50; |
--min-lines | -l :最小代码行数,默认为5; |
--max-lines | -x : 最大代码行数,默认为1000; |
--max-size | -z :最大文件大小,单位为kb,默认100; |
--threshold | -t :重复级别的阈值,当项目重复级别大于该阈值时报错退出,默认为空; |
--ignore | -i :忽略的文件类型; |
--reporters | -r :输出类型 |
参考文献