從 0 搭建 Vite 3 + Vue 3 前端工程化項(xiàng)目
前言
Vue 3 正式版已經(jīng)發(fā)布有一段時(shí)間了,隨著 Vite 腳手架注定成為下一代前端工具鏈,許多用戶都想基于 Vite 來構(gòu)建 Vue 項(xiàng)目,如果想基于 Vite 構(gòu)建 Vue 3 項(xiàng)目,社區(qū)模板完全滿足您的需求,如果想構(gòu)建 Vite 3 + Vue 3 + JavaScript 項(xiàng)目,那社區(qū)模板不太能滿足您的需求,因?yàn)樯鐓^(qū)模板提供 Vue 3 項(xiàng)目幾乎是基于 Vite 2 + TypeScript 構(gòu)建,對(duì)于不熟悉 TypeScript 語言的用戶不是很友好,因此接下來從 0 開始手把手帶大家搭建一套規(guī)范的 Vite 3 + Vue 3 + JavaScript 前端工程化項(xiàng)目環(huán)境。
本文章篇幅較長(zhǎng),從以下幾個(gè)方面展開:
基礎(chǔ)搭建 代碼規(guī)范 提交規(guī)范 自動(dòng)部署
本項(xiàng)目完整代碼托管在 GitHub 倉(cāng)庫(kù)[1],歡迎點(diǎn)亮小星星 ????
技術(shù)棧
?? Vite 3[2] - 構(gòu)建工具(就是快?。?/section> ?? Vue 3[3] - 漸進(jìn)式 JavaScript 框架 ?? Vue Router[4] - 官方路由管理器 ?? Pinia[5] - 值得你喜歡的 Vue Store ?? TDesign[6] - TDesign 適配桌面端的組件庫(kù) ?? Less[7] - CSS 預(yù)處理器 ?? Axios[8] - 一個(gè)基于 promise 的網(wǎng)絡(luò)請(qǐng)求庫(kù),可以用于瀏覽器和 node.js ?? Husky[9] + Lint-Staged[10] - Git Hook 工具 ??? EditorConfig[11] + ESLint[12] + Prettier[13] + Stylelint[14] - 代碼規(guī)范 ?? Commitizen[15] + Commitlint[16] - 提交規(guī)范 ?? GitHub Actions[17] - 自動(dòng)部署
基礎(chǔ)搭建
構(gòu)建項(xiàng)目雛形
確保你安裝了最新版本的 Node.js[18],然后在命令行中運(yùn)行以下命令:
# 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
復(fù)制代碼
這一指令將會(huì)安裝并執(zhí)行 create-vite[19],它是一個(gè)基本模板快速啟動(dòng)項(xiàng)目工具。

在項(xiàng)目被創(chuàng)建后,通過以下步驟安裝依賴并啟動(dòng)開發(fā)服務(wù)器:
# 打開項(xiàng)目
cd <your-project-name>
# 安裝依賴
npm install
# 啟動(dòng)項(xiàng)目
npm run dev
復(fù)制代碼

Vite 基礎(chǔ)配置
Vite 配置文件 vite.config.js 位于項(xiàng)目根目錄下,項(xiàng)目啟動(dòng)時(shí)會(huì)自動(dòng)讀取。
本項(xiàng)目針對(duì)公共基礎(chǔ)路徑、自定義路徑別名、服務(wù)器選項(xiàng)、構(gòu)建選項(xiàng)等做了如下基礎(chǔ)配置:
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,
// 端口號(hào)
port: 3000,
// 監(jiān)聽所有地址
host: '0.0.0.0',
// 服務(wù)啟動(dòng)時(shí)是否自動(dòng)打開瀏覽器
open: true,
// 允許跨域
cors: true,
// 自定義代理規(guī)則
proxy: {},
},
build: {
// 設(shè)置最終構(gòu)建的瀏覽器兼容目標(biāo)
target: 'es2015',
// 構(gòu)建后是否生成 source map 文件
sourcemap: false,
// chunk 大小警告的限制(以 kbs 為單位)
chunkSizeWarningLimit: 2000,
// 啟用/禁用 gzip 壓縮大小報(bào)告
reportCompressedSize: false,
},
});
復(fù)制代碼
關(guān)于 Vite 更多配置項(xiàng)及用法,請(qǐng)查看 Vite 官網(wǎng) vitejs.dev/config/[20] 。
規(guī)范目錄結(jié)構(gòu)
├── dist/
└── src/
├── api/ // 接口請(qǐng)求目錄
├── assets/ // 靜態(tài)資源目錄
├── common/ // 通用類庫(kù)目錄
├── components/ // 公共組件目錄
├── router/ // 路由配置目錄
├── store/ // 狀態(tài)管理目錄
├── style/ // 通用樣式目錄
├── utils/ // 工具函數(shù)目錄
├── views/ // 頁面組件目錄
├── App.vue
├── main.js
├── tests/ // 單元測(cè)試目錄
├── index.html
├── jsconfig.json // JavaScript 配置文件
├── vite.config.js // Vite 配置文件
└── package.json
復(fù)制代碼
集成 Vue Router 路由工具
安裝依賴
npm i vue-router@4
復(fù)制代碼
創(chuàng)建路由配置文件
在 src/router 目錄下新建 index.js 文件與 modules 文件夾
└── src/
├── router/
├── modules/ // 路由模塊
├── index.js // 路由配置文件
復(fù)制代碼
關(guān)于路由表,建議根據(jù)功能的不同來拆分到 modules 文件夾中,好處是:
方便后期維護(hù)
減少 Git 合并代碼沖突可能性
export default [
{
path: '/',
name: 'home',
component: () => import('@/views/HomeView.vue'),
},
{
path: '/about',
name: 'about',
component: () => import('@/views/AboutView.vue'),
},
];
復(fù)制代碼
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;
復(fù)制代碼
根據(jù)路由配置的實(shí)際情況,需要在 src 下創(chuàng)建 views 目錄,用來存儲(chǔ)頁面組件。
掛載路由配置
在 main.js 文件中掛載路由配置
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');
復(fù)制代碼
集成 Pinia 全局狀態(tài)管理工具
安裝依賴
npm i pinia
復(fù)制代碼
創(chuàng)建倉(cāng)庫(kù)配置文件
在 src/store 目錄下新建 index.js 文件與 modules 文件夾
└── src/
├── store/
├── modules/ // 倉(cāng)庫(kù)模塊
├── index.js // 倉(cāng)庫(kù)配置文件
復(fù)制代碼
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 1,
}),
actions: {
accumulate() {
this.count++;
},
},
});
復(fù)制代碼
import { createPinia } from 'pinia';
const store = createPinia();
export default store;
export * from './modules/counter';
復(fù)制代碼
開發(fā)中需要將不同功能所對(duì)應(yīng)的狀態(tài),拆分到不同的 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');
復(fù)制代碼
集成 TDesign Vue Next 組件庫(kù)
安裝依賴
npm i tdesign-vue-next
復(fù)制代碼
基礎(chǔ)使用
import { createApp } from 'vue';
import TDesign from 'tdesign-vue-next';
// 引入組件庫(kù)全局樣式資源
import 'tdesign-vue-next/es/style/index.css';
const app = createApp(App);
app.use(TDesign);
復(fù)制代碼
按需引入
使用 unplugin-vue-components 和 unplugin-auto-import 來實(shí)現(xiàn)自動(dòng)導(dǎo)入:
npm install unplugin-vue-components unplugin-auto-import -D
復(fù)制代碼
在 Vite 對(duì)應(yīng)的配置文件 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'
})],
}),
],
};
復(fù)制代碼
集成 Axios HTTP 工具
安裝依賴
npm i axios
復(fù)制代碼
請(qǐng)求配置
在 utils 目錄下創(chuàng)建 request.js 文件,配置好適合自己業(yè)務(wù)的請(qǐng)求攔截和響應(yīng)攔截:
└── src/
├── api // 接口
├── utils/
├── request.js // axios 請(qǐng)求庫(kù)二次封裝
復(fù)制代碼
import axios from 'axios';
// 創(chuàng)建請(qǐng)求實(shí)例
const instance = axios.create({
baseURL: '/api',
// 指定請(qǐng)求超時(shí)的毫秒數(shù)
timeout: 1000,
// 表示跨域請(qǐng)求時(shí)是否需要使用憑證
withCredentials: false,
});
// 前置攔截器(發(fā)起請(qǐng)求之前的攔截)
instance.interceptors.request.use(
(config) => {
/**
* 在這里一般會(huì)攜帶前臺(tái)的參數(shù)發(fā)送給后臺(tái),比如下面這段代碼:
* const token = getToken()
* if (token) {
* config.headers.token = token
* }
*/
return config;
},
(error) => {
return Promise.reject(error);
},
);
// 后置攔截器(獲取到響應(yīng)時(shí)的攔截)
instance.interceptors.response.use(
(response) => {
/**
* 根據(jù)你的項(xiàng)目實(shí)際情況來對(duì) response 和 error 做處理
* 這里對(duì) 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);
},
);
// 導(dǎo)出常用函數(shù)
/**
* @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;
復(fù)制代碼
之后在 api 文件夾中以業(yè)務(wù)模型對(duì)接口進(jìn)行拆分,舉個(gè)例子,將所有跟用戶相關(guān)接口封裝在 User 類中,此類稱作用戶模型。
在 User 類中比如有登錄、注冊(cè)、獲取用戶信息等方法,如果有業(yè)務(wù)邏輯變動(dòng),只需要修改相關(guān)方法即可。
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,
});
}
}
復(fù)制代碼
把每個(gè)業(yè)務(wù)模型獨(dú)立成一個(gè) js 文件,聲明一個(gè)類通過其屬性和方法來實(shí)現(xiàn)這個(gè)模型相關(guān)的數(shù)據(jù)獲取,這樣可以大大提升代碼的可讀性與可維護(hù)性。
模擬演示
在需要使用接口的地方,引入對(duì)應(yīng)的業(yè)務(wù)模型文件,參考如下:
<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>
復(fù)制代碼
集成 CSS 預(yù)處理器 Less
本項(xiàng)目使用 CSS 預(yù)處理器 Less,直接安裝為開發(fā)依賴即可。
Vite 內(nèi)部已幫我們集成了相關(guān)的 loader,不需要額外配置。
安裝依賴
npm i less -D
復(fù)制代碼
如何使用
在 <style></style> 樣式標(biāo)簽中引用 lang="less" 即可。
<style lang="less"></style>
復(fù)制代碼
CSS 命名規(guī)范推薦 BEM 命名規(guī)范
參考鏈接:CSS BEM 書寫規(guī)范[21]
全局樣式
在 src/style 目錄下創(chuàng)建 variables.less 全局樣式文件:
└── src/
├── style/
├── variables.less // 全局樣式文件
復(fù)制代碼
在 vite.config.js 配置文件中新增CSS 預(yù)處理器相關(guān)配置即可實(shí)現(xiàn) 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,
},
},
},
});
復(fù)制代碼
樣式穿透
官方文檔[22]
在 Vue3 中,改變了以往樣式穿透的語法,如果繼續(xù)使用 ::v-deep、/deep/、>>> 等語法的話,會(huì)出現(xiàn)一個(gè)警告,下面是新的語法:
/* 深度選擇器 */
:deep(selector) {
/* ... */
}
/* 插槽選擇器 */
:slotted(selector) {
/* ... */
}
/* 全局選擇器 */
:global(selector) {
/* ... */
}
復(fù)制代碼
至此,一個(gè)基于 JavaScript + Vite3 + Vue3 + Vue Router + Pinia + Axios + Less 的前端項(xiàng)目開發(fā)環(huán)境搭建完畢。
項(xiàng)目托管在 GitHub 倉(cāng)庫(kù)[23],需要的同學(xué)可以去下載下來,參考學(xué)習(xí)。
接下來增加代碼規(guī)范約束、提交規(guī)范約束、單元測(cè)試、自動(dòng)部署等,讓其更完善、更健壯。
代碼規(guī)范
隨著前端應(yīng)用逐漸變得大型化和復(fù)雜化,在同一個(gè)項(xiàng)目中有多個(gè)人員參與時(shí),每個(gè)人的前端能力程度不等,他們往往會(huì)用不同的編碼風(fēng)格和習(xí)慣在項(xiàng)目中寫代碼,長(zhǎng)此下去,勢(shì)必會(huì)讓項(xiàng)目的健壯性越來越差。解決這些問題,理論上講,口頭約定和代碼審查都可以,但是這種方式無法實(shí)時(shí)反饋,而且溝通成本過高,不夠靈活,更關(guān)鍵的是無法把控。不以規(guī)矩,不能成方圓,我們不得不在項(xiàng)目使用一些工具來約束代碼規(guī)范。
本文講解如何使用 EditorConfig + ESLint + Prettier + Stylelint 組合來實(shí)現(xiàn)代碼規(guī)范化。
這樣做帶來好處:
解決團(tuán)隊(duì)之間代碼不規(guī)范導(dǎo)致的可讀性差和可維護(hù)性差的問題。 解決團(tuán)隊(duì)成員不同編輯器導(dǎo)致的編碼規(guī)范不統(tǒng)一問題。 提前發(fā)現(xiàn)代碼風(fēng)格問題,給出對(duì)應(yīng)規(guī)范提示,及時(shí)修復(fù)。 減少代碼審查過程中反反復(fù)復(fù)的修改過程,節(jié)約時(shí)間。 自動(dòng)格式化,統(tǒng)一編碼風(fēng)格,從此和臟亂差的代碼說再見。
集成 EditorConfig 配置
EditorConfig[24] 主要用于統(tǒng)一不同 IDE 編輯器的編碼風(fēng)格。
在項(xiàng)目根目錄下添加 .editorconfig 文件:
# 表示是最頂層的 EditorConfig 配置文件
root = true
# 表示所有文件適用
[*]
# 縮進(jìn)風(fēng)格(tab | space)
indent_style = space
# 控制換行類型(lf | cr | crlf)
end_of_line = lf
# 設(shè)置文件字符集為 utf-8
charset = utf-8
# 去除行首的任意空白字符
trim_trailing_whitespace = true
# 始終在文件末尾插入一個(gè)新行
insert_final_newline = true
# 表示僅 md 文件適用以下規(guī)則
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# 表示僅 ts、js、vue、css 文件適用以下規(guī)則
[*.{ts,js,vue,css}]
indent_size = 2
復(fù)制代碼
很多 IDE 中會(huì)默認(rèn)支持此配置,但是也有些不支持,如:VSCode、Atom、Sublime Text 等。
具體列表可以參考官網(wǎng),如果在 VSCode 中使用需要安裝
EditorConfig for VS Code插件。

集成 ESLint 配置
ESLint[25] 是針對(duì) EScript 的一款代碼檢測(cè)工具,它可以檢測(cè)項(xiàng)目中編寫不規(guī)范的代碼,如果寫出不符合規(guī)范的代碼會(huì)被警告。
由此我們就可以借助于 ESLint 強(qiáng)大的功能來統(tǒng)一團(tuán)隊(duì)的編碼規(guī)范。
安裝依賴
`ESLint`[26] - ESLint 本體 `eslint-define-config`[27] - 改善 ESLint 規(guī)范編寫體驗(yàn) `eslint-plugin-vue`[28] - 適用于 Vue 文件的 ESLint 插件 `eslint-config-airbnb-base`[29] - Airbnb JavaScript 風(fēng)格指南 `eslint-plugin-import`[30] - 使用 eslint-config-airbnb-base時(shí)必須安裝的前置插件`vue-eslint-parser`[31] - 使用 eslint-plugin-vue時(shí)必須安裝的 ESLint 解析器
npm i eslint eslint-define-config eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue vue-eslint-parser -D
復(fù)制代碼
安裝插件
Visual Studio Code 編輯器使用 ESLint 配置需要下載插件 ESLint 。

JetBrains 系列編輯器(WebStorm、IntelliJ IDEA 等)則不用額外安裝插件。
創(chuàng)建 ESLint 配置文件
在項(xiàng)目根目錄創(chuàng)建 .eslintrc.js 文件,并填入以下內(nèi)容:
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,
// 確保在導(dǎo)入路徑內(nèi)一致使用文件擴(kuò)展名
'import/extensions': 0,
// 確保導(dǎo)入指向可以解析的文件/模塊
'import/no-unresolved': 0,
// 首選默認(rèn)導(dǎo)出導(dǎo)入/首選默認(rèn)導(dǎo)出
'import/prefer-default-export': 0,
// 要求使用 let 或 const 而不是 var
'no-var': 'error',
// 禁止使用 new 以避免產(chǎn)生副作用
'no-new': 1,
// 禁止變量聲明與外層作用域的變量同名
'no-shadow': 0,
// 禁用 console
'no-console': 0,
// 禁止標(biāo)識(shí)符中有懸空下劃線
'no-underscore-dangle': 0,
// 禁止在可能與比較操作符相混淆的地方使用箭頭函數(shù)
'no-confusing-arrow': 0,
// 禁用一元操作符 ++ 和 --
'no-plusplus': 0,
// 禁止對(duì) function 的參數(shù)進(jìn)行重新賦值
'no-param-reassign': 0,
// 禁用特定的語法
'no-restricted-syntax': 0,
// 禁止在變量定義之前使用它們
'no-use-before-define': 0,
// 禁止直接調(diào)用 Object.prototypes 的內(nèi)置屬性
'no-prototype-builtins': 0,
// 禁止可以在有更簡(jiǎn)單的可替代的表達(dá)式時(shí)使用三元操作符
'no-unneeded-ternary': 'error',
// 禁止重復(fù)模塊導(dǎo)入
'no-duplicate-imports': 'error',
// 禁止在對(duì)象中使用不必要的計(jì)算屬性
'no-useless-computed-key': 'error',
// 強(qiáng)制使用一致的縮進(jìn)
indent: ['error', 2],
// 強(qiáng)制使用駱駝拼寫法命名約定
camelcase: 0,
// 強(qiáng)制類方法使用 this
'class-methods-use-this': 0,
// 要求構(gòu)造函數(shù)首字母大寫
'new-cap': 0,
// 強(qiáng)制一致地使用 function 聲明或表達(dá)式
'func-style': 0,
// 強(qiáng)制一行的最大長(zhǎng)度
'max-len': 0,
// 要求 return 語句要么總是指定返回的值,要么不指定
'consistent-return': 0,
// 強(qiáng)制switch要有default分支
'default-case': 2,
// 強(qiáng)制剩余和擴(kuò)展運(yùn)算符及其表達(dá)式之間有空格
'rest-spread-spacing': 'error',
// 要求使用 const 聲明那些聲明后不再被修改的變量
'prefer-const': 'error',
// 強(qiáng)制箭頭函數(shù)的箭頭前后使用一致的空格
'arrow-spacing': 'error',
},
overrides: [
{
files: ['*.vue'],
rules: {
// 要求組件名稱總是多個(gè)單詞
'vue/multi-word-component-names': 0,
},
},
],
});
復(fù)制代碼
關(guān)于更多配置項(xiàng)信息,請(qǐng)前往 ESLint 官網(wǎng)查看 ESLint-Configuring[32]
創(chuàng)建 ESLint 過濾規(guī)則
在項(xiàng)目根目錄添加一個(gè) .eslintignore 文件,內(nèi)容如下:
dist
node_modules
!.prettierrc.js
復(fù)制代碼
集成 Prettier 配置
Prettier[33] 是一款強(qiáng)大的代碼格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等語言,基本上前端能用到的文件格式它都可以搞定,是當(dāng)下最流行的代碼格式化工具。
安裝依賴
npm i prettier -D
復(fù)制代碼
安裝插件
Visual Studio Code 編輯器使用 Prettier 配置需要下載插件 Prettier - Code formatter 。

JetBrains 系列編輯器(WebStorm、IntelliJ IDEA 等)則不用額外安裝插件,可直接使用 Prettier 配置。
創(chuàng)建 Prettier 配置文件
Prettier 支持多種格式的配置文件[34],比如 .json、.yml、.yaml、.js等。
在項(xiàng)目根目錄創(chuàng)建 .prettierrc.js 文件,并填入以下內(nèi)容:
module.exports = {
// 一行最多 120 字符
printWidth: 120,
// 使用 2 個(gè)空格縮進(jìn)
tabWidth: 2,
// 不使用縮進(jìn)符,而使用空格
useTabs: false,
// 行尾需要有分號(hào)
semi: true,
// 使用單引號(hào)
singleQuote: true,
// 對(duì)象的 key 僅在必要時(shí)用引號(hào)
quoteProps: 'as-needed',
// jsx 不使用單引號(hào),而使用雙引號(hào)
jsxSingleQuote: false,
// 末尾需要有逗號(hào)
trailingComma: 'all',
// 大括號(hào)內(nèi)的首尾需要空格
bracketSpacing: true,
// jsx 標(biāo)簽的反尖括號(hào)需要換行
jsxBracketSameLine: false,
// 箭頭函數(shù),只有一個(gè)參數(shù)的時(shí)候,也需要括號(hào)
arrowParens: 'always',
// 每個(gè)文件格式化的范圍是文件的全部?jī)?nèi)容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要寫文件開頭的 @prettier
requirePragma: false,
// 不需要自動(dòng)在文件開頭插入 @prettier
insertPragma: false,
// 使用默認(rèn)的折行標(biāo)準(zhǔn)
proseWrap: 'preserve',
// 根據(jù)顯示樣式?jīng)Q定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// vue 文件中的 script 和 style 內(nèi)不用縮進(jìn)
vueIndentScriptAndStyle: false,
// 換行符使用 lf
endOfLine: 'lf',
// 格式化嵌入的內(nèi)容
embeddedLanguageFormatting: 'auto',
// html, vue, jsx 中每個(gè)屬性占一行
singleAttributePerLine: false,
};
復(fù)制代碼
關(guān)于更多配置項(xiàng)信息,請(qǐng)前往 Prettier 官網(wǎng)查看 Prettier-Options[35]
創(chuàng)建 Prettier 過濾規(guī)則
在項(xiàng)目根目錄添加一個(gè) .prettierignore 文件,內(nèi)容如下:
## OS
.DS_Store
.idea
.editorconfig
pnpm-lock.yaml
.npmrc
# Ignored suffix
*.log
*.md
*.svg
*.png
*.ico
*ignore
## Local
.husky
## Built-files
.cache
dist
復(fù)制代碼
解決 Prettier 和 ESLint 沖突
本項(xiàng)目中的 ESLint 配置使用了 Airbnb JavaScript 風(fēng)格指南校驗(yàn),其規(guī)則之一是_代碼結(jié)束后面要加分號(hào)_,而在 Prettier 配置文件中加了_代碼結(jié)束后面不加分號(hào)_配置項(xiàng),從而沖突了。
解決兩者沖突問題,需要用到 eslint-plugin-prettier 和 eslint-config-prettier。
eslint-plugin-prettier將 Prettier 的規(guī)則設(shè)置到 ESLint 的規(guī)則中eslint-config-prettier關(guān)閉 ESLint 中與 Prettier 中會(huì)發(fā)生沖突的規(guī)則
最后形成優(yōu)先級(jí):Prettier 配置規(guī)則 > ESLint 配置規(guī)則
安裝依賴
npm i eslint-plugin-prettier eslint-config-prettier -D
復(fù)制代碼
修改 ESLint 配置文件
修改 .eslintrc.js 文件,在 extends 中添加 plugin:prettier/recommended 規(guī)則(此規(guī)則一定要加在最后)。
module.exports = {
extends: [
'airbnb-base',
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended'
],
}
復(fù)制代碼
自動(dòng)格式化代碼
Visual Studio Code 在 settings.json 設(shè)置文件中,增加以下代碼:
{
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.fixAll.eslint": true
}
}
復(fù)制代碼
WebStorm 打開設(shè)置窗口,按如下操作,最后點(diǎn)擊 Apply -> OK:

集成 Stylelint 配置
Stylelint 是一個(gè)強(qiáng)大、先進(jìn)的 CSS 代碼檢查器(linter),可以幫助你規(guī)避 CSS 代碼中的錯(cuò)誤并保持一致的編碼風(fēng)格。
安裝依賴
`Stylelint`[36] - Stylelint 本體 `stylelint-config-prettier`[37] - 關(guān)閉 Stylelint 中與 Prettier 中會(huì)發(fā)生沖突的規(guī)則 `stylelint-config-standard`[38] - Stylelint 官方推薦規(guī)則 `stylelint-config-recommended-vue`[39] - 檢驗(yàn) vue 文件中的樣式 `stylelint-order`[40] - CSS 屬性順序規(guī)則插件
npm i stylelint stylelint-config-prettier stylelint-config-standard stylelint-config-recommended-vue stylelint-order -D
復(fù)制代碼
安裝插件
Visual Studio Code 編輯器使用 Stylelint 配置需要下載插件 Stylelint 。

JetBrains 系列編輯器(WebStorm、IntelliJ IDEA 等)則不用額外安裝插件。
創(chuàng)建 Stylelint 配置文件
在項(xiàng)目根目錄創(chuàng)建 .stylelintrc.js 文件,并填入以下內(nèi)容:
module.exports = {
root: true,
defaultSeverity: 'error',
extends: [
'stylelint-config-standard',
'stylelint-config-prettier'
],
plugins: ['stylelint-order'],
rules: {
// 不允許未知函數(shù)
'function-no-unknown': null,
// 指定類選擇器的模式
'selector-class-pattern': null,
// 禁止空源碼
'no-empty-source': null,
// 指定字符串使用單引號(hào)
'string-quotes': 'single',
// 禁止未知的@規(guī)則
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'tailwind',
'apply',
'variants',
'responsive',
'screen',
'function',
'if',
'each',
'include',
'mixin',
],
},
],
// 指定@規(guī)則名的大小寫
'at-rule-name-case': 'lower',
// 指定縮進(jìn)
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',
],
},
}
復(fù)制代碼
創(chuàng)建 Stylelint 過濾規(guī)則
在項(xiàng)目根目錄添加一個(gè) .stylelintignore 文件,內(nèi)容如下:
# .stylelintignore
# 舊的不需打包的樣式庫(kù)
*.min.css
# 其他類型文件
*.js
*.jpg
*.woff
# 測(cè)試和打包目錄
/test/
/dist/*
/public/*
public/*
/node_modules/
復(fù)制代碼
啟用 Vue 文件支持
Stylelint v14 版本默認(rèn)不支持 vue 文件中的 style 代碼自動(dòng)檢測(cè),詳情查看官方遷移指南[41]
安裝依賴
`stylelint-config-html`[42] - 解析 vue 文件 `postcss-html`[43] - 使用 stylelint-config-html依賴的模塊`postcss-less`[44] - 對(duì) less 文件進(jìn)行解析
npm i stylelint-config-html postcss-html postcss-less -D
復(fù)制代碼
修改 Stylelint 配置文件
修改 .stylelintrc.js 文件,添加如下配置:
module.exports = {
overrides: [
{
files: ['*.vue', '**/*.vue', '*.html', '**/*.html'],
extends: ['stylelint-config-html'],
rules: {
// 指定關(guān)鍵幀名稱的模式
'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'],
},
],
};
復(fù)制代碼
修改 Visual Studio Code 工作區(qū)配置
Visual Studio Code 在 settings.json 設(shè)置文件中,增加以下代碼:
{
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"]
}
復(fù)制代碼
集成 husky 和 lint-staged
在項(xiàng)目中已集成 ESLint 和 Prettier,在編碼時(shí),這些工具可以對(duì)代碼進(jìn)行實(shí)時(shí)校驗(yàn),在一定程度上能有效規(guī)范所寫代碼,但有些人可能覺得這些限制很麻煩,從而選擇視“提示”而不見,依舊按自己編程風(fēng)格來寫代碼,或者干脆禁用掉這些工具,開發(fā)完成就直接把代碼提交到了倉(cāng)庫(kù),日積月累,ESLint 也就形同虛設(shè)。
所以,還需要做一些限制,讓沒通過 ESLint 檢測(cè)和修復(fù)的代碼禁止提交,從而保證倉(cāng)庫(kù)代碼都是符合規(guī)范的。
為了解決這個(gè)問題,需要用到 Git Hook,在本地執(zhí)行 git commit 的時(shí)候,就對(duì)所提交的代碼進(jìn)行 ESLint 檢測(cè)和修復(fù)(即執(zhí)行 eslint \--fix),如果這些代碼沒通過 ESLint 規(guī)則校驗(yàn),則禁止提交。
實(shí)現(xiàn)這一功能,需要借助 husky[45] + lint-staged[46] 。
配置 husky
注意:本項(xiàng)目使用 husky 6.x 版本,6.x 版本配置方式跟之前版本有較大差異,當(dāng)發(fā)現(xiàn)配置方法不一致時(shí),一切以 husky 官網(wǎng)[47]為準(zhǔn)。
使用 husky-init 命令快速在項(xiàng)目初始化 husky 配置:
# 初始化倉(cāng)庫(kù)
git init
# 初始化
npx husky-init
# 安裝依賴
npm install
復(fù)制代碼
husky 包含很多 hook(鉤子),常用有:pre-commit、commit-msg。
使用 pre-commit 來觸發(fā) ESLint 命令,修改 .husky/pre-commit 文件觸發(fā)命令:
eslint --fix ./src --ext .vue,.js,.ts
復(fù)制代碼
pre-commit hook 文件作用是:當(dāng)執(zhí)行 git commit \-m "xxx" 時(shí),會(huì)先對(duì) src 目錄下所有的 .vue、.js、.ts 文件執(zhí)行 eslint \--fix 命令,如果 ESLint 通過,成功 commit,否則終止 commit。
但是又存在一個(gè)問題:有時(shí)候明明只改動(dòng)了一兩個(gè)文件,卻要對(duì)所有的文件執(zhí)行 eslint \--fix。
假如這是一個(gè)歷史項(xiàng)目,在中途配置了 ESLint 規(guī)則,那么在提交代碼時(shí),也會(huì)對(duì)其他未修改的“歷史”文件都進(jìn)行檢查,可能會(huì)造成大量文件出現(xiàn) ESLint 錯(cuò)誤,顯然這不是我們想要的結(jié)果。
所以只需要用 ESLint 修復(fù)此次寫的代碼,而不去影響其他的代碼,此時(shí)需要借助 lint-staged 工具。
配置 lint-staged
lint-staged 一般結(jié)合 husky 來使用,它可以讓 husky 的 hook 觸發(fā)的命令只作用于 git 暫存區(qū)的文件,而不會(huì)影響到其他文件。
安裝依賴
npm i lint-staged -D
復(fù)制代碼
新增配置
在 package.json 里增加 lint-staged 配置項(xiàng):
{
"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"
]
},
}
復(fù)制代碼
修改觸發(fā)命令
修改 .husky/pre-commit 文件觸發(fā)命令為:
npx lint-staged
復(fù)制代碼

經(jīng)過以上配置之后,就可以在每次提交之前對(duì)所有代碼進(jìn)行格式化,保證線上代碼的規(guī)范性。
提交規(guī)范
多人協(xié)作項(xiàng)目中,在提交代碼環(huán)節(jié),也存在一種情況:不能保證每個(gè)人對(duì)提交信息的準(zhǔn)確描述,因此會(huì)出現(xiàn)提交信息紊亂、風(fēng)格不一致的情況。
如果 git commit 的描述信息精準(zhǔn),在后期維護(hù)和 Bug 處理時(shí)會(huì)變得有據(jù)可查,項(xiàng)目開發(fā)周期內(nèi)還可以根據(jù)規(guī)范的提交信息快速生成開發(fā)日志,從而方便我們追蹤項(xiàng)目和把控進(jìn)度。
社區(qū)最流行、最知名、最受認(rèn)可的 Angular[48] 團(tuán)隊(duì)提交規(guī)范:

參考鏈接: Angular Style Commit Message Conventions[49]
Commit Message 格式規(guī)范
commit message 由 Header、Body、Footer 組成。
<Header>
<Body>
<Footer>
復(fù)制代碼
Header
Header 部分包括三個(gè)字段 type(必需)、scope(可選)和 subject(必需)。
<type>(<scope>): <subject>
復(fù)制代碼
type
type 用于說明 commit 的提交類型(必須是以下幾種之一)。
| 值 | 描述 |
|---|---|
| feat | 新增功能 |
| fix | 修復(fù)問題 |
| docs | 文檔變更 |
| style | 代碼格式(不影響功能,例如空格、分號(hào)等格式修正) |
| refactor | 代碼重構(gòu) |
| perf | 改善性能 |
| test | 測(cè)試 |
| build | 變更項(xiàng)目構(gòu)建或外部依賴(例如 scopes: webpack、gulp、npm 等) |
| ci | 更改持續(xù)集成軟件的配置文件和 package 中的 scripts 命令,例如 scopes: Travis, Circle 等 |
| chore | 變更構(gòu)建流程或輔助工具 |
| revert | 代碼回退 |
scope
scope 用于指定本次 commit 影響的范圍。
scope 依據(jù)項(xiàng)目而定,例如在業(yè)務(wù)項(xiàng)目中可以依據(jù)菜單或者功能模塊劃分,如果是組件庫(kù)開發(fā),則可以依據(jù)組件劃分。
subject
subject 是本次 commit 的簡(jiǎn)潔描述,長(zhǎng)度約定在 50 個(gè)字符以內(nèi),通常遵循以下幾個(gè)規(guī)范:
用動(dòng)詞開頭,第一人稱現(xiàn)在時(shí)表述,例如:change 代替 changed 或 changes 第一個(gè)字母小寫 結(jié)尾不加句號(hào)(.)
Body
body 是對(duì)本次 commit 的詳細(xì)描述,可以分成多行。
跟 subject 類似,用動(dòng)詞開頭,body 應(yīng)該說明修改的原因和更改前后的行為對(duì)比。
Footer
如果本次提交的代碼是突破性的變更或關(guān)閉缺陷,則 Footer 必需,否則可以省略。
突破性的變更
當(dāng)前代碼與上一個(gè)版本有突破性改變,則 Footer 以 BREAKING CHANGE 開頭,后面是對(duì)變動(dòng)的描述、以及變動(dòng)的理由。
關(guān)閉缺陷
如果當(dāng)前提交是針對(duì)特定的 issue,那么可以在 Footer 部分填寫需要關(guān)閉的單個(gè) 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)
復(fù)制代碼fix
fix(compile): couple of unit tests for IE9
Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.
Closes #392
Breaks foo.bar api, foo.baz should be used instead
復(fù)制代碼style
style(location): add couple of missing semi colons
復(fù)制代碼chore
chore(release): v3.4.2
復(fù)制代碼
集成 cz-git 實(shí)現(xiàn)規(guī)范提交
一款工程性更強(qiáng),輕量級(jí),高度自定義,標(biāo)準(zhǔn)輸出格式的 commitizen[50] 適配器
官方網(wǎng)站:cz-git[51]

安裝依賴
npm install -D cz-git
復(fù)制代碼
指定適配器
修改 package.json 文件,添加 config 指定使用的適配器
{
"scripts": {},
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
}
}
復(fù)制代碼
自定義配置(可選)
cz-git 與 commitlint[52] 進(jìn)行聯(lián)動(dòng)給予校驗(yàn)信息,所以可以編寫于 commitlint[53] 配置文件之中。
例如:(? 配置模板[54])
/** @type {import('cz-git').UserConfig} */
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {},
prompt: {
useEmoji: false,
emojiAlign: 'center',
allowCustomIssuePrefixs: false,
allowEmptyIssuePrefixs: false,
},
};
復(fù)制代碼
本項(xiàng)目配置文件可參考:commitlint.config.js[55]
全局使用
全局安裝的好處在于:在任何項(xiàng)目下都可以利用
cz或git cz命令啟動(dòng)命令行工具,生成標(biāo)準(zhǔn)化 commit message
安裝全局依賴
npm install -g cz-git commitizen
復(fù)制代碼
全局配置適配器類型
echo '{ "path": "cz-git" }' > ~/.czrc
復(fù)制代碼
自定義配置(可選)
方式一: 編輯 ~/.czrc 文件以 json 形式添加配置,例如:
{
"path": "cz-git",
"useEmoji": true
}
復(fù)制代碼
方式二:與 commitlint[56] 配合,在 $HOME 路徑下創(chuàng)建配置文件 (↓ 配置模板[57])
集成 commitlint 驗(yàn)證規(guī)范提交
在“代碼規(guī)范”章節(jié)中提到,盡管制定了規(guī)范,但在多人協(xié)作的項(xiàng)目中,總有些人依舊我行我素。
因此提交代碼這個(gè)環(huán)節(jié),也增加一個(gè)限制:只讓符合 Angular 規(guī)范的 commit message 通過。
此功能需借助 @commitlint/config-conventional 和 @commitlint/cli 工具來實(shí)現(xiàn)。
安裝
`@commitlint/cli`[58] - Commitlint 本體 `@commitlint/config-conventional`[59] - 通用提交規(guī)范
npm i @commitlint/cli @commitlint/config-conventional -D
復(fù)制代碼
配置
在項(xiàng)目根目錄創(chuàng)建 commitlint.config.js 文件,并填入以下內(nèi)容:
module.exports = {
extends: ['@commitlint/config-conventional']
}
復(fù)制代碼
使用 husky 命令在 .husky 目錄下創(chuàng)建 commit-msg 文件,并在此執(zhí)行驗(yàn)證命令:
npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
復(fù)制代碼

本項(xiàng)目完整代碼托管在 GitHub 倉(cāng)庫(kù)[60],歡迎點(diǎn)亮小星星 ????
自動(dòng)部署
本章節(jié)將介紹如何使用 CI(Continuous Integration 持續(xù)集成)服務(wù)來完成項(xiàng)目部署工作。
常見的 CI 工具有 GitHub Actions、GitLab CI、Travis CI、Circle CI 等。
本項(xiàng)目使用 GitHub Actions 來完成這一操作。
?? 參考鏈接:GitHub Actions 入門教程[61]
創(chuàng)建 GitHub 倉(cāng)庫(kù)
因?yàn)?GitHub Actions 只對(duì) GitHub 倉(cāng)庫(kù)有效,所以創(chuàng)建 GitHub 倉(cāng)庫(kù)[62]來托管項(xiàng)目代碼。

master分支存儲(chǔ)項(xiàng)目源代碼gh-pages分支存儲(chǔ)打包后的靜態(tài)文件
創(chuàng)建 GitHub Token
創(chuàng)建一個(gè)有 repo 和 workflow 權(quán)限的 GitHub Token[63]

注意:新生成的 Token 只會(huì)顯示一次。

添加 Actions secret
將上述創(chuàng)建的 Token 添加到 GitHub 倉(cāng)庫(kù)中的 Secrets 里,并將這個(gè)新增的 secret 命名為 VITE_VUE_DEPLOY 。
步驟:倉(cāng)庫(kù) -> Settings -> Secrets -> Actions -> New repository secret。

注意:新創(chuàng)建的 secret
VITE_VUE_DEPLOY在 Actions 配置文件中要用到,兩個(gè)地方需保持一致!
修改 package.json
打開 package.json 文件,新增 homepage 字段,表示該應(yīng)用發(fā)布后的根目錄(參見官方文檔[64])。
"homepage": "https://[username].github.io/github-actions-demo",
復(fù)制代碼
上面代碼中,將 [username] 替換成你的 GitHub 用戶名,參見范例[65]。
創(chuàng)建 Actions 配置文件
(1)在項(xiàng)目根目錄下創(chuàng)建 .github 目錄。
(2)在 .github 目錄下創(chuàng)建 workflows 目錄。
(3)在 workflows 目錄下創(chuàng)建 deploy.yml 文件。

name: Vite Vue Deploy
on:
push:
# master 分支有 push 時(shí)觸發(fā)
branches: [master]
jobs:
deploy:
# 指定虛擬機(jī)環(huán)境
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x]
steps:
- name: Checkout
# 拉取 GitHub 倉(cāng)庫(kù)代碼
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
# 設(shè)定 Node.js 環(huán)境
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
復(fù)制代碼
?? 通過此鏈接 ElanYoung.github.io/vite-vue-js…[66] 即可訪問本項(xiàng)目
文章總結(jié)
本文從技術(shù)選項(xiàng)到架構(gòu)搭建、從代碼規(guī)范約束到提交信息規(guī)范約束,一步一步帶領(lǐng)大家如何從一個(gè)最簡(jiǎn)單的前端項(xiàng)目骨架到規(guī)范的前端工程化環(huán)境,基本涵蓋前端項(xiàng)目開發(fā)的整個(gè)流程,特別適合剛接觸前端工程化的同學(xué)學(xué)習(xí)。
因篇幅較長(zhǎng),所涉及技術(shù)點(diǎn)較多,難免會(huì)出現(xiàn)錯(cuò)誤,希望大家多多指正,謝謝大家!
關(guān)于本文
作者:威廉王子
https://juejin.cn/post/7156957907890733063
