从 0 搭建 Vite 3 + Vue 3 前端工程化项目

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 前端工程化项目环境。

本文章篇幅较长,从以下几个方面展开:

  • 基础搭建
  • 代码规范
  • 提交规范
  • 自动部署
技术栈
  • ⚡️ Vite 3[2] - 构建工具(就是快!)
  • 🖖 Vue 3[3] - 渐进式 JavaScript 框架
  • 🚦 Vue Router[4] - 官方路由管理器
  • 📦 Pinia[5] - 值得你喜欢的 Vue Store
  • 💻 TDesign[6] - TDesign 适配桌面端的组件库
  • 🎨 Less[7] - CSS 预处理器
  • 🔗 Axios[8] - 一个基于 promise 的网络请求库,可以用于浏览器和 node.js
  • 🧰 Husky[9] + Lint-Staged[10] - Git Hook 工具
  • 🛡️ EditorConfig[11] + ESLint[12] + Prettier[13] + Stylelint[14] - 代码规范
  • 🔨 Commitizen[15] + Commitlint[16] - 提交规范
  • 💡 GitHub Actions[17] - 自动部署
基础搭建

确保你安装了最新版本的 Node.js[18],然后在命令行中运行以下命令:

# npm 6.x
npm create vite@latest vite-vue-js-template --template vue

# npm 7+, 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,它是一个基本模板快速启动项目工具。

在项目被创建后,通过以下步骤安装依赖并启动开发服务器:

# 打开项目
cd <your-project-name>

# 安装依赖
npm install

# 启动项目
npm run dev
Vite 基础配置

Vite 配置文件 vite.config.js 位于项目根目录下,项目启动时会自动读取。

本项目针对公共基础路径、自定义路径别名、服务器选项、构建选项等做了如下基础配置:

import { defineConfig } from 'vite';
import { resolve } from 'path';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
base: './',
plugins: [
  vue(),
],
resolve: {
  alias: {
    '@': resolve(__dirname, './src') ,
  },
},
server: {
  // 是否开启 https
  https: false,
  // 端口号
  port: 3000,
  // 监听所有地址
  host: '0.0.0.0',
  // 服务启动时是否自动打开浏览器
  open: true,
  // 允许跨域
  cors: true,
  // 自定义代理规则
  proxy: {},
},
build: {
  // 设置最终构建的浏览器兼容目标
  target: 'es2015',
  // 构建后是否生成 source map 文件
  sourcemap: false,
  //  chunk 大小警告的限制(以 kbs 为单位)
  chunkSizeWarningLimit: 2000,
  // 启用/禁用 gzip 压缩大小报告
  reportCompressedSize: false,
  },
 });
规范目录结构
 ├── 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
集成 Vue Router 路由工具
安装依赖
npm i vue-router@4
创建路由配置文件

在 src/router 目录下新建 index.js 文件与 modules 文件夹

└── src/
        ├── router/
            ├── modules/  // 路由模块
            ├── index.js  // 路由配置文件

关于路由表,建议根据功能的不同来拆分到 modules 文件夹中,好处是:

  • 方便后期维护

  • 减少 Git 合并代码冲突可能性

      export default [
    {
      path: '/',
      name: 'home',
      component: () => import('@/views/HomeView.vue'),
    },
    {
     path: '/about',
     name: 'about',
     component: () => import('@/views/AboutView.vue'),
     },
    ];
    
      import { createRouter, createWebHistory } from 'vue-router';
      import baseRouters from './modules/base';
      const routes = [...baseRouters];
      const router = createRouter({
      history: createWebHistory(import.meta.env.BASE_URL),
      routes,
     scrollBehavior() {
      return {
       el: '#app',
       top: 0,
       behavior: 'smooth',
         };
       },
     });
    
     export default router;
    

根据路由配置的实际情况,需要在 src 下创建 views 目录,用来存储页面组件。

挂载路由配置

在 main.js 文件中挂载路由配置

 import { createApp } from 'vue';
 import App from './App.vue';
 import router from './router';
 createApp(App).use(router).mount('#app');
集成 Pinia 全局状态管理工具
  • 安装依赖

      npm i pinia
    
创建仓库配置文件

在 src/store 目录下新建 index.js 文件与 modules 文件夹

└── src/
   ├── store/
        ├── modules/  // 仓库模块
        ├── index.js  // 仓库配置文件

 import { defineStore } from 'pinia';
 export const useCounterStore = defineStore('counter', {
 state: () => ({
   count: 1,
 }),
 actions: {
  accumulate() {
    this.count++;
  },
 },
});


 import { createPinia } from 'pinia';
 const store = createPinia();
 export default store;
 export * from './modules/counter';

开发中需要将不同功能所对应的状态,拆分到不同的 modules,好处如同路由模块一样。

挂载 Pinia 配置

在 main.js 文件中挂载 Vuex 配置

import { createApp } from 'vue';
import App from './App.vue';
import store from './store';
import router from './router';
createApp(App).use(router).use(store).mount('#app');
集成 TDesign Vue Next 组件库
安装依赖
 npm i tdesign-vue-next
基础使用
 import { createApp } from 'vue';
 import TDesign from 'tdesign-vue-next';
 // 引入组件库全局样式资源
 import 'tdesign-vue-next/es/style/index.css';
 const app = createApp(App);
 app.use(TDesign);
按需引入

使用 unplugin-vue-components 和 unplugin-auto-import 来实现自动导入:

  npm install unplugin-vue-components unplugin-auto-import -D

在 Vite 对应的配置文件 vite.config.js 添加上述插件:

import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { TDesignResolver } from 'unplugin-vue-components/resolvers';

export default {
  plugins: [
   AutoImport({
    resolvers: [TDesignResolver({
      library: 'vue-next'
   })],
   }),
   Components({
     resolvers: [TDesignResolver({
       library: 'vue-next'
     })],
   }),
  ],
 };
集成 Axios HTTP 工具
安装依赖
 npm i axios
请求配置

在 utils 目录下创建 request.js 文件,配置好适合自己业务的请求拦截和响应拦截:

  └── src/
            ├── api  // 接口
       ├── utils/
            ├── request.js  // axios 请求库二次封装

  import axios from 'axios';
  // 创建请求实例
  const instance = axios.create({
    baseURL: '/api',
   // 指定请求超时的毫秒数
    timeout: 1000,
   // 表示跨域请求时是否需要使用凭证
   withCredentials: false,
  });

 // 前置拦截器(发起请求之前的拦截)
  instance.interceptors.request.use(
   (config) => {
    /**
     * 在这里一般会携带前台的参数发送给后台,比如下面这段代码:
     * const token = getToken()
     * if (token) {
     *  config.headers.token = token
     * }
     */
    return config;
   },
  (error) => {
     return Promise.reject(error);
    },
 );

  // 后置拦截器(获取到响应时的拦截)
 instance.interceptors.response.use(
   (response) => {
/**
 * 根据你的项目实际情况来对 response 和 error 做处理
 * 这里对 response 和 error 不做任何处理,直接返回
 */
   return response;
  },
  (error) => {
  const { response } = error;
  if (response && response.data) {
    return Promise.reject(error);
  }
  const { message } = error;
  console.error(message);
  return Promise.reject(error);
  },
 );

 // 导出常用函数
/**
  * @param {string} url
  * @param {object} data
  * @param {object} params
  */
 export function post(url, data = {}, params = {}) {
   return instance({
   method: 'post',
   url,
   data,
   params,
   });
 }

/**
  * @param {string} url
  * @param {object} params
 */
 export function get(url, params = {}) {
   return instance({
    method: 'get',
    url,
    params,
  });
}

/**
  * @param {string} url
  * @param {object} data
  * @param {object} params
  */
 export function put(url, data = {}, params = {}) {
      return instance({
       method: 'put',
       url,
       params,
       data,
   });
  }

 /**
  * @param {string} url
  * @param {object} params
  */
 export function _delete(url, params = {}) {
  return instance({
  method: 'delete',
  url,
  params,
  });
 }

export default instance;

之后在 api 文件夹中以业务模型对接口进行拆分,举个例子,将所有跟用户相关接口封装在 User 类中,此类称作用户模型。

在 User 类中比如有登录、注册、获取用户信息等方法,如果有业务逻辑变动,只需要修改相关方法即可。

 import { post } from '@/utils/request';
 export default class User {
  /**
   * 登录
  * @param {String} username 用户名
  * @param {String} password 密码
  * @returns
  */
  static async login(username, password) {
   return post('/login', {
    username,
     password,
   });
  }
 }

把每个业务模型独立成一个 js 文件,声明一个类通过其属性和方法来实现这个模型相关的数据获取,这样可以大大提升代码的可读性与可维护性。

模拟演示

在需要使用接口的地方,引入对应的业务模型文件,参考如下:

 <script>
 import User from '@/api/user';
 export default {
  data() {
    return {
      username: '',
      password: '',
   };
 },
 methods: {
 async login() {
   const res = await User.login(this.username, this.password);
   console.log(res);
 },
 },
};
</script>
集成 CSS 预处理器 Less

本项目使用 CSS 预处理器 Less,直接安装为开发依赖即可。

Vite 内部已帮我们集成了相关的 loader,不需要额外配置。

安装依赖
  npm i less -D
如何使用

在 <style></style> 样式标签中引用 lang="less" 即可。

 <style lang="less"></style>

CSS 命名规范推荐 BEM 命名规范

全局样式

在 src/style 目录下创建 variables.less 全局样式文件:

 └── src/
     ├── style/
         ├── variables.less  // 全局样式文件

在 vite.config.js 配置文件中新增CSS 预处理器相关配置即可实现 less 全局样式:

import { resolve } from 'path';
export default defineConfig({
css: {
  preprocessorOptions: {
    less: {
      modifyVars: {
        hack: `true; @import (reference) "${resolve('src/style/variables.less')}";`,
      },
      math: 'strict',
      javascriptEnabled: true,
       },
     },
  },
});
样式穿透

在 Vue3 中,改变了以往样式穿透的语法,如果继续使用 ::v-deep、/deep/、>>> 等语法的话,会出现一个警告,下面是新的语法:

/* 深度选择器 */
 :deep(selector) {
    /* ... */
 }

/* 插槽选择器 */
:slotted(selector) {
   /* ... */
 }

/* 全局选择器 */
:global(selector) {
 /* ... */
}

至此,一个基于 JavaScript + Vite3 + Vue3 + Vue Router + Pinia + Axios + Less 的前端项目开发环境搭建完毕。

接下来增加代码规范约束、提交规范约束、单元测试、自动部署等,让其更完善、更健壮。

代码规范

随着前端应用逐渐变得大型化和复杂化,在同一个项目中有多个人员参与时,每个人的前端能力程度不等,他们往往会用不同的编码风格和习惯在项目中写代码,长此下去,势必会让项目的健壮性越来越差。解决这些问题,理论上讲,口头约定和代码审查都可以,但是这种方式无法实时反馈,而且沟通成本过高,不够灵活,更关键的是无法把控。不以规矩,不能成方圆,我们不得不在项目使用一些工具来约束代码规范。

本文讲解如何使用 EditorConfig + ESLint + Prettier + Stylelint 组合来实现代码规范化。

这样做带来好处:

  • 解决团队之间代码不规范导致的可读性差和可维护性差的问题。
  • 解决团队成员不同编辑器导致的编码规范不统一问题。
  • 提前发现代码风格问题,给出对应规范提示,及时修复。
  • 减少代码审查过程中反反复复的修改过程,节约时间。
  • 自动格式化,统一编码风格,从此和脏乱差的代码说再见。

EditorConfig 主要用于统一不同 IDE 编辑器的编码风格。

在项目根目录下添加 .editorconfig 文件:

 # 表示是最顶层的 EditorConfig 配置文件
   root = true
 # 表示所有文件适用
  [*]
 # 缩进风格(tab | space)
  indent_style = space
 # 控制换行类型(lf | cr | crlf)
  end_of_line = lf
# 设置文件字符集为 utf-8
  charset = utf-8
# 去除行首的任意空白字符
  trim_trailing_whitespace = true
# 始终在文件末尾插入一个新行
  insert_final_newline = true
# 表示仅 md 文件适用以下规则
 [*.md]
 max_line_length = off
 trim_trailing_whitespace = false
# 表示仅 ts、js、vue、css 文件适用以下规则
 [*.{ts,js,vue,css}]
 indent_size = 2

很多 IDE 中会默认支持此配置,但是也有些不支持,如:VSCode、Atom、Sublime Text 等。具体列表可以参考官网,如果在 VSCode 中使用需要安装 EditorConfig for VS Code 插件。

集成 ESLint 配置

ESLint 是针对 EScript 的一款代码检测工具,它可以检测项目中编写不规范的代码,如果写出不符合规范的代码会被警告。

由此我们就可以借助于 ESLint 强大的功能来统一团队的编码规范。

安装依赖
  • ESLint - ESLint 本体

  • eslint-define-config - 改善 ESLint 规范编写体验

  • eslint-plugin-vue - 适用于 Vue 文件的 ESLint 插件

  • eslint-config-airbnb-base - Airbnb JavaScript 风格指南

  • eslint-plugin-import - 使用 eslint-config-airbnb-base 时必须安装的前置插件

  • vue-eslint-parser - 使用 eslint-plugin-vue 时必须安装的 ESLint 解析器

       npm i eslint eslint-define-config eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue vue-eslint-parser -D
    
安装插件

Visual Studio Code 编辑器使用 ESLint 配置需要下载插件 ESLint 。

JetBrains 系列编辑器(WebStorm、IntelliJ IDEA 等)则不用额外安装插件。

创建 ESLint 配置文件

在项目根目录创建 .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 过滤规则

在项目根目录添加一个 .eslintignore 文件,内容如下:

dist
node_modules
!.prettierrc.js
集成 Prettier 配置
 npm i prettier -D

安装插件

Visual Studio Code 编辑器使用 Prettier 配置需要下载插件 Prettier - Code formatter 。

JetBrains 系列编辑器(WebStorm、IntelliJ IDEA 等)则不用额外安装插件,可直接使用 Prettier 配置。

创建 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,
 // 不需要写文件开头的 @prettier
 requirePragma: false,
 // 不需要自动在文件开头插入 @prettier
 insertPragma: false,
 // 使用默认的折行标准
 proseWrap: 'preserve',
 // 根据显示样式决定 html 要不要折行
  htmlWhitespaceSensitivity: 'css',
 // vue 文件中的 script 和 style 内不用缩进
 vueIndentScriptAndStyle: false,
 // 换行符使用 lf
 endOfLine: 'lf',
 // 格式化嵌入的内容
 embeddedLanguageFormatting: 'auto',
 // html, vue, jsx 中每个属性占一行
 singleAttributePerLine: false,
};

关于更多配置项信息,请前往 Prettier 官网查看

创建 Prettier 过滤规则

在项目根目录添加一个 .prettierignore 文件,内容如下:

## OS
.DS_Store
.idea
.editorconfig
 pnpm-lock.yaml
.npmrc

# Ignored suffix
*.log
*.md
*.svg
*.png
*.ico
*ignore

## Local
.husky
## Built-files
.cache
 dist
解决 Prettier 和 ESLint 冲突

本项目中的 ESLint 配置使用了 Airbnb JavaScript 风格指南校验,其规则之一是代码结束后面要加分号,而在 Prettier 配置文件中加了代码结束后面不加分号配置项,从而冲突了。

解决两者冲突问题,需要用到 eslint-plugin-prettier 和 eslint-config-prettier。

  • eslint-plugin-prettier 将 Prettier 的规则设置到 ESLint 的规则中
  • eslint-config-prettier 关闭 ESLint 中与 Prettier 中会发生冲突的规则
    最后形成优先级:Prettier 配置规则 > ESLint 配置规则
安装依赖
   npm i eslint-plugin-prettier eslint-config-prettier -D
修改 ESLint 配置文件

修改 .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
  }
}
集成 Stylelint 配置

Stylelint 是一个强大、先进的 CSS 代码检查器(linter),可以帮助你规避 CSS 代码中的错误并保持一致的编码风格。

安装依赖
  • Stylelint - Stylelint 本体

  • stylelint-config-prettier - 关闭 Stylelint 中与 Prettier 中会发生冲突的规则

  • stylelint-config-standard - Stylelint 官方推荐规则

  • stylelint-config-recommended-vue - 检验 vue 文件中的样式

  • stylelint-order - CSS 属性顺序规则插件

    npm i stylelint stylelint-config-prettier stylelint-config-standard stylelint-config-recommended-vue stylelint-order -D
    
安装插件

Visual Studio Code 编辑器使用 Stylelint 配置需要下载插件 Stylelint 。

JetBrains 系列编辑器(WebStorm、IntelliJ IDEA 等)则不用额外安装插件。

创建 Stylelint 配置文件

在项目根目录创建 .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',
  ],
  },
 }
创建 Stylelint 过滤规则

在项目根目录添加一个 .stylelintignore 文件,内容如下:

 # .stylelintignore
 # 旧的不需打包的样式库
 *.min.css

 # 其他类型文件
*.js
*.jpg
*.woff

# 测试和打包目录
/test/
/dist/*
/public/*
public/*
/node_modules/
启用 Vue 文件支持

Stylelint v14 版本默认不支持 vue 文件中的 style 代码自动检测

安装依赖

stylelint-config-html - 解析 vue 文件
postcss-html - 使用 stylelint-config-html 依赖的模块
postcss-less - 对 less 文件进行解析

npm i stylelint-config-html postcss-html postcss-less -D

修改 Stylelint 配置文件
修改 .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 工作区配置
Visual Studio Code 在 settings.json 设置文件中,增加以下代码:

 {
   "stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"]
 }
集成 husky 和 lint-staged

在项目中已集成 ESLint 和 Prettier,在编码时,这些工具可以对代码进行实时校验,在一定程度上能有效规范所写代码,但有些人可能觉得这些限制很麻烦,从而选择视“提示”而不见,依旧按自己编程风格来写代码,或者干脆禁用掉这些工具,开发完成就直接把代码提交到了仓库,日积月累,ESLint 也就形同虚设。

所以,还需要做一些限制,让没通过 ESLint 检测和修复的代码禁止提交,从而保证仓库代码都是符合规范的。

为了解决这个问题,需要用到 Git Hook,在本地执行 git commit 的时候,就对所提交的代码进行 ESLint 检测和修复(即执行 eslint --fix),如果这些代码没通过 ESLint 规则校验,则禁止提交。

配置 husky

注意:本项目使用 husky 6.x 版本,6.x 版本配置方式跟之前版本有较大差异,当发现配置方法不一致时,一切以 husky 官网为准。

使用 husky-init 命令快速在项目初始化 husky 配置:

# 初始化仓库
 git init

 # 初始化
 npx husky-init

# 安装依赖
 npm install

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

lint-staged 一般结合 husky 来使用,它可以让 husky 的 hook 触发的命令只作用于 git 暂存区的文件,而不会影响到其他文件。

安装依赖
 npm i lint-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 文件触发命令为:

npx lint-staged

经过以上配置之后,就可以在每次提交之前对所有代码进行格式化,保证线上代码的规范性。

提交规范

多人协作项目中,在提交代码环节,也存在一种情况:不能保证每个人对提交信息的准确描述,因此会出现提交信息紊乱、风格不一致的情况。

如果 git commit 的描述信息精准,在后期维护和 Bug 处理时会变得有据可查,项目开发周期内还可以根据规范的提交信息快速生成开发日志,从而方便我们追踪项目和把控进度。

Commit Message 格式规范

commit message 由 Header、Body、Footer 组成。

 <Header>

 <Body>

 <Footer>
Header

Header 部分包括三个字段 type(必需)、scope(可选)和 subject(必需)。

 <type>(<scope>): <subject>
type

type 用于说明 commit 的提交类型(必须是以下几种之一)。

scope

scope 用于指定本次 commit 影响的范围。

scope 依据项目而定,例如在业务项目中可以依据菜单或者功能模块划分,如果是组件库开发,则可以依据组件划分。

subject

subject 是本次 commit 的简洁描述,长度约定在 50 个字符以内,通常遵循以下几个规范:

  • 用动词开头,第一人称现在时表述,例如:change 代替 changed 或 changes
  • 第一个字母小写
  • 结尾不加句号(.)
Body

body 是对本次 commit 的详细描述,可以分成多行。

跟 subject 类似,用动词开头,body 应该说明修改的原因和更改前后的行为对比。

Footer

如果本次提交的代码是突破性的变更或关闭缺陷,则 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 available

Breaks $browser.onHashChange, which was removed (use onUrlChange instead)

集成 cz-git 实现规范提交

一款工程性更强,轻量级,高度自定义,标准输出格式的 commitizen 适配器
官方网站:cz-git

安装依赖
 npm install -D cz-git
指定适配器

修改 package.json 文件,添加 config 指定使用的适配器

 {
   "scripts": {},
   "config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
   }
 }
自定义配置(可选)

cz-git 与 commitlint 进行联动给予校验信息,所以可以编写于 commitlint 配置文件之中。

例如:(⇒ 配置模板)

 /** @type {import('cz-git').UserConfig} */
module.exports = {
 extends: ['@commitlint/config-conventional'],
 rules: {},
 prompt: {
  useEmoji: false,
  emojiAlign: 'center',
  allowCustomIssuePrefixs: false,
  allowEmptyIssuePrefixs: false,
  },
};
全局使用

全局安装的好处在于:在任何项目下都可以利用 cz 或 git cz 命令启动命令行工具,生成标准化 commit message

安装全局依赖
npm install -g cz-git commitizen
全局配置适配器类型
echo '{ "path": "cz-git" }' > ~/.czrc
自定义配置(可选)

方式一: 编辑 ~/.czrc 文件以 json 形式添加配置,例如:

{
  "path": "cz-git",
  "useEmoji": true
}

方式二:与 commitlint 配合,在 $HOME 路径下创建配置文件 (↓ 配置模板)

集成 commitlint 验证规范提交

在“代码规范”章节中提到,尽管制定了规范,但在多人协作的项目中,总有些人依旧我行我素。

因此提交代码这个环节,也增加一个限制:只让符合 Angular 规范的 commit message 通过。

此功能需借助 @commitlint/config-conventional 和 @commitlint/cli 工具来实现。

安装
  • @commitlint/cli - Commitlint 本体

  • @commitlint/config-conventional - 通用提交规范

    npm i @commitlint/cli @commitlint/config-conventional -D
    
配置

在项目根目录创建 commitlint.config.js 文件,并填入以下内容:

module.exports = {
   extends: ['@commitlint/config-conventional']
 }

使用 husky 命令在 .husky 目录下创建 commit-msg 文件,并在此执行验证命令:

 npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
自动部署

本章节将介绍如何使用 CI(Continuous Integration 持续集成)服务来完成项目部署工作。

常见的 CI 工具有 GitHub Actions、GitLab CI、Travis CI、Circle CI 等。

本项目使用 GitHub Actions 来完成这一操作。

参考链接:GitHub Actions 入门教程
创建 GitHub 仓库

因为 GitHub Actions 只对 GitHub 仓库有效,所以创建 GitHub 仓库来托管项目代码。

  • master 分支存储项目源代码
  • gh-pages 分支存储打包后的静态文件
创建 GitHub Token

创建一个有 repo 和 workflow 权限的 GitHub Token

注意:新生成的 Token 只会显示一次。

添加 Actions secret

将上述创建的 Token 添加到 GitHub 仓库中的 Secrets 里,并将这个新增的 secret 命名为 VITE_VUE_DEPLOY 。

步骤:仓库 -> Settings -> Secrets -> Actions -> New repository secret。

注意:新创建的 secret VITE_VUE_DEPLOY 在 Actions 配置文件中要用到,两个地方需保持一致!

修改 package.json

打开 package.json 文件,新增 homepage 字段,表示该应用发布后的根目录(参见官方文档)。

"homepage": "https://[username].github.io/github-actions-demo",

上面代码中,将 [username] 替换成你的 GitHub 用户名,参见范例。

创建 Actions 配置文件

(1)在项目根目录下创建 .github 目录。

(2)在 .github 目录下创建 workflows 目录。

(3)在 workflows 目录下创建 deploy.yml 文件。

name: Vite Vue Deploy

on:
push:
# master 分支有 push 时触发
branches: [master]

 jobs:
 deploy:
# 指定虚拟机环境
runs-on: ubuntu-latest

strategy:
  matrix:
    node-version: [14.x, 16.x]

steps:
  - name: Checkout
    # 拉取 GitHub 仓库代码
    uses: actions/checkout@v3

  - name: Use Node.js ${{ matrix.node-version }}
    # 设定 Node.js 环境
    uses: actions/setup-node@v3
    with:
      node-version: ${{ matrix.node-version }}

  - name: Install
    # 安装依赖
    run: npm install

  - name: Build
    # 打包
    run: npm run build

  - name: Deploy
    uses: JamesIves/github-pages-deploy-action@v4
    with:
      # 部署打包目录
      folder: dist
      # 密钥名
      token: ${{ secrets.VITE_VUE_DEPLOY }}
      # 分支
      branch: gh-pages

项目git仓库: https://github.com/ElanYoung/vite-vue-js-starter-template

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容