從 0 開始手把手帶你搭建一套規範的 Vue3 項目工程環境

Vue3 跟 Vite 正式版發佈有很長一段時間了,生態圈也漸漸豐富起來,作者已在多個項目中使用,總結一下:就是快!也不用擔心穩定性問題,開發體驗真不是一般好!還沒嘗試的同學可以從本文開始學習,從 0 開始手把手帶你搭建一套基於 Vite + Vue3 + TypeScript 規範的前端工程化項目環境。

本文篇幅較長,從以下幾個方面展開:

本項目完整的代碼託管在 GitHub 倉庫 [1] 歡迎點亮小星星 🌟🌟

技術棧

架構搭建

請確保你的電腦上成功安裝 Node.js,本項目使用 Vite 構建工具,需要 Node.js 版本 >= 12.0.0

查看 Node.js 版本:

node -v

建議將 Node.js 升級到最新的穩定版本:

# 使用 nvm 安裝最新穩定版 Node.js
nvm install stable

使用 Vite 快速初始化項目雛形

然後按照終端提示完成以下操作:

  1. 輸入項目名稱

    例如:本項目名稱 vite-vue3-starter

    image

  2. 選擇模板

    本項目需要使用 Vue3 + TypeScript,所以我們選擇 vue-ts,會自動安裝 Vue3 和 TypeScript。

    image

    image

    你還可以通過附加的命令行選項直接指定項目名和模板,本項目要構建 Vite + Vue3 + TypeScript 項目,則運行:

    # npm 6.x
    npm init @vitejs/app vite-vue3-starter --template vue-ts
        
    # npm 7+(需要額外的雙橫線)
    npm init @vitejs/app vite-vue3-starter -- --template vue-ts
        
    # yarn
    yarn create @vitejs/app vite-vue3-starter --template vue-ts
  3. 安裝依賴

    npm install
  4. 啓動項目

    npm run dev

    image

    如上圖,表示 Vite + Vue3 + TypeScript 簡單的項目骨架搭建完畢,下面我們來爲這個項目集成 Vue Router、Vuex、Element Plus、Axios、Stylus/Sass/Less。

修改 Vite 配置文件

Vite 配置文件 vite.config.ts 位於根目錄下,項目啓動時會自動讀取。

本項目先做一些簡單配置,例如:設置 @ 指向 src 目錄、 服務啓動端口、打包路徑、代理等。

關於 Vite 更多配置項及用法,請查看 Vite 官網 https://vitejs.dev/config/ 。

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 如果編輯器提示 path 模塊找不到,則可以安裝一下 @types/node -> npm i @types/node -D
import { resolve } from "path";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      "@": resolve(__dirname, "src"), // 設置 `@` 指向 `src` 目錄
    },
  },
  base: "./", // 設置打包路徑
  server: {
    port: 4000, // 設置服務啓動端口號
    open: true, // 設置服務啓動時是否自動打開瀏覽器
    cors: true, // 允許跨域

    // 設置代理,根據我們項目實際情況配置
    // proxy: {
    //   '/api'{
    //     target: 'http://xxx.xxx.xxx.xxx:8000',
    //     changeOrigin: true,
    //     secure: false,
    //     rewrite: (path) => path.replace('/api/''/')
    //   }
    // }
  },
});

規範目錄結構

├── publish/
└── src/
    ├── assets/                    // 靜態資源目錄
    ├── common/                    // 通用類庫目錄
    ├── components/                // 公共組件目錄
    ├── router/                    // 路由配置目錄
    ├── store/                     // 狀態管理目錄
    ├── style/                     // 通用 CSS 目錄
    ├── utils/                     // 工具函數目錄
    ├── views/                     // 頁面組件目錄
    ├── App.vue
    ├── main.ts
    ├── shims-vue.d.ts
├── tests/                         // 單元測試目錄
├── index.html
├── tsconfig.json                  // TypeScript 配置文件
├── vite.config.ts                 // Vite 配置文件
└── package.json

集成路由工具 Vue Router

  1. 安裝支持 Vue3 的路由工具 vue-router@4

    npm i vue-router@4
  2. 創建 src/router/index.ts 文件

    src 下創建 router 目錄,然後在 router 目錄裏新建 index.ts 文件:

     └── src/
         ├── router/
             ├── index.ts  // 路由配置文件
    import {
      createRouter,
      createWebHashHistory,
      RouteRecordRaw,
    } from "vue-router";
    import Home from "@/views/home.vue";
    import Vuex from "@/views/vuex.vue";
        
    const routes: Array<RouteRecordRaw> = [
      {
        path: "/",
        name: "Home",
        component: Home,
      },
      {
        path: "/vuex",
        name: "Vuex",
        component: Vuex,
      },
      {
        path: "/axios",
        name: "Axios",
        component: () => import("@/views/axios.vue"), // 懶加載組件
      },
    ];
        
    const router = createRouter({
      history: createWebHashHistory(),
      routes,
    });
        
    export default router;

    根據本項目路由配置的實際情況,你需要在 src 下創建 views 目錄,用來存儲頁面組件。

    我們在 views 目錄下創建 home.vuevuex.vueaxios.vue

  3. main.ts 文件中掛載路由配置

    import { createApp } from "vue";
    import App from "./App.vue";
        
    import router from "./router/index";
        
    createApp(App).use(router).mount("#app");

集成狀態管理工具 Vuex

  1. 安裝支持 Vue3 的狀態管理工具 vuex@next

    npm i vuex@next
  2. 創建 src/store/index.ts 文件

    src 下創建 store 目錄,然後在 store 目錄裏新建 index.ts 文件:

    └── src/
        ├── store/
            ├── index.ts  // store 配置文件
    import { createStore } from "vuex";
        
    const defaultState = {
      count: 0,
    };
        
    // Create a new store instance.
    export default createStore({
      state() {
        return defaultState;
      },
      mutations: {
        increment(state: typeof defaultState) {
          state.count++;
        },
      },
      actions: {
        increment(context) {
          context.commit("increment");
        },
      },
      getters: {
        double(state: typeof defaultState) {
          return 2 * state.count;
        },
      },
    });
  3. main.ts 文件中掛載 Vuex 配置

    import { createApp } from "vue";
    import App from "./App.vue";
        
    import store from "./store/index";
        
    createApp(App).use(store).mount("#app");

集成 UI 框架 Element Plus

  1. 安裝支持 Vue3 的 UI 框架 Element Plus

    npm i element-plus
  2. main.ts 文件中掛載 Element Plus

    import { createApp } from "vue";
    import App from "./App.vue";
        
    import ElementPlus from "element-plus";
    import "element-plus/lib/theme-chalk/index.css";
        
    createApp(App).use(ElementPlus).mount("#app");

集成 HTTP 工具 Axios

  1. 安裝 Axios(Axios 跟 Vue 版本沒有直接關係,安裝最新即可)

    npm i axios
  2. 配置 Axios

    爲了使項目的目錄結構合理且規範,我們在 src 下創建 utils 目錄來存儲我們常用的工具函數。

    Axios 作爲 HTTP 工具,我們在 utils 目錄下創建 axios.ts 作爲 Axios 配置文件:

    └── src/
        ├── utils/
            ├── axios.ts  // Axios 配置文件
    import Axios from "axios";
    import { ElMessage } from "element-plus";
        
    const baseURL = "https://api.github.com";
        
    const axios = Axios.create({
      baseURL,
      timeout: 20000, // 請求超時 20s
    });
        
    // 前置攔截器(發起請求之前的攔截)
    axios.interceptors.request.use(
      (response) ={
        /**
         * 根據你的項目實際情況來對 config 做處理
         * 這裏對 config 不做任何處理,直接返回
         */
        return response;
      },
      (error) ={
        return Promise.reject(error);
      }
    );
        
    // 後置攔截器(獲取到響應時的攔截)
    axios.interceptors.response.use(
      (response) ={
        /**
         * 根據你的項目實際情況來對 response 和 error 做處理
         * 這裏對 response 和 error 不做任何處理,直接返回
         */
        return response;
      },
      (error) ={
        if (error.response && error.response.data) {
          const code = error.response.status;
          const msg = error.response.data.message;
          ElMessage.error(`Code: ${code}, Message: ${msg}`);
          console.error(`[Axios Error]`, error.response);
        } else {
          ElMessage.error(`${error}`);
        }
        return Promise.reject(error);
      }
    );
        
    export default axios;
  3. 使用 Axios
    在需要使用 Axios 文件裏,引入 Axios 配置文件,參考如下:

    <template></template>
    <script lang="ts">
      import { defineComponent } from "vue";
      import axios from "../utils/axios";
        
      export default defineComponent({
        setup() {
          axios
            .get("/users/XPoet")
            .then((res) ={
              console.log("res: ", res);
            })
            .catch((err) ={
              console.log("err: ", err);
            });
        },
      });
    </script>

集成 CSS 預編譯器 Stylus/Sass/Less

本項目使用 CSS 預編譯器 Stylus,直接安裝爲開發依賴即可。Vite 內部已幫我們集成了相關的 loader,不需要額外配置。同理,你也可以使用 Sass 或 Less 等。

  1. 安裝

    npm i stylus -D
    # or
    npm i sass -D
    npm i less -D
  2. 使用

    <style lang="stylus">
      ...
    </style>

至此,一個基於 TypeScript + Vite + Vue3 + Vue Router + Vuex + Element Plus + Axios + Stylus/Sass/Less 的前端項目開發環境搭建完畢,項目 Demo 託管在 GitHub 倉庫 [26],需要的同學可以去下載下來,參考學習。

下面我們來打磨這個項目,增加代碼規範約束、提交規範約束、單元測試、自動部署等,讓其更完善、更健壯。

代碼規範

隨着前端應用逐漸變得大型化和複雜化,在同一個項目中有多個人員參與時,每個人的前端能力程度不等,他們往往會用不同的編碼風格和習慣在項目中寫代碼,長此下去,勢必會讓項目的健壯性越來越差。解決這些問題,理論上講,口頭約定和代碼審查都可以,但是這種方式無法實時反饋,而且溝通成本過高,不夠靈活,更關鍵的是無法把控。不以規矩,不能成方圓,我們不得不在項目使用一些工具來約束代碼規範。

本文講解如何使用 EditorConfig + Prettier + ESLint 組合來實現代碼規範化。

這樣做帶來好處:

集成 EditorConfig 配置

EditorConfig 有助於爲不同 IDE 編輯器上處理同一項目的多個開發人員維護一致的編碼風格。

官網:http://editorconfig.org

在項目根目錄下增加 .editorconfig 文件:

# Editor configuration, see http://editorconfig.org

# 表示是最頂層的 EditorConfig 配置文件
root = true

[*] # 表示所有文件適用
charset = utf-8 # 設置文件字符集爲 utf-8
indent_style = space # 縮進風格(tab | space)
indent_size = 2 # 縮進大小
end_of_line = lf # 控制換行類型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始終在文件末尾插入一個新行

[*.md] # 表示僅 md 文件適用以下規則
max_line_length = off
trim_trailing_whitespace = false

注意:

集成 Prettier 配置

Prettier 是一款強大的代碼格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等語言,基本上前端能用到的文件格式它都可以搞定,是當下最流行的代碼格式化工具。

官網:https://prettier.io/

  1. 安裝 Prettier

    npm i prettier -D
  2. 創建 Prettier 配置文件

    Prettier 支持多種格式的配置文件 [27],比如 .json.yml.yaml.js等。

    在本項目根目錄下創建 .prettierrc 文件。

  3. 配置 .prettierrc

    在本項目中,我們進行如下簡單配置,關於更多配置項信息,請前往官網查看 Prettier-Options[28] 。

    {
      "useTabs": false,
      "tabWidth": 2,
      "printWidth": 100,
      "singleQuote": true,
      "trailingComma""none",
      "bracketSpacing": true,
      "semi"false
    }
  4. Prettier 安裝且配置好之後,就能使用命令來格式化代碼

    # 格式化所有文件(. 表示所有文件)
    npx prettier --write .

注意:

Prettier 配置好以後,在使用 VSCode 或 WebStorm 等編輯器的格式化功能時,編輯器就會按照 Prettier 配置文件的規則來進行格式化,避免了因爲大家編輯器配置不一樣而導致格式化後的代碼風格不統一的問題。

集成 ESLint 配置

ESLint[29] 是一款用於查找並報告代碼中問題的工具,並且支持部分問題自動修復。其核心是通過對代碼解析得到的 AST(Abstract Syntax Tree 抽象語法樹)進行模式匹配,來分析代碼達到檢查代碼質量和風格問題的能力。

正如前面我們提到的因團隊成員之間編程能力和編碼習慣不同所造成的代碼質量問題,我們使用 ESLint 來解決,一邊寫代碼一邊查找問題,如果發現錯誤,就給出規則提示,並且自動修復,長期下去,可以促使團隊成員往同一種編碼風格靠攏。

  1. 安裝 ESLint

    可以全局或者本地安裝,作者推薦本地安裝(只在當前項目中安裝)。

    npm i eslint -D
  2. 配置 ESLint

    ESLint 安裝成功後,執行 npx eslint --init,然後按照終端操作提示完成一系列設置來創建配置文件。

  1. ESLint 配置文件 .eslintrc.js

    上一步操作完成後,會在項目根目錄下自動生成 .eslintrc.js 配置文件:

    module.exports = {
      env: {
        browser: true,
        es2021: true,
        node: true,
      },
      extends: ["plugin:vue/essential""airbnb-base"],
      parserOptions: {
        ecmaVersion: 12,
        parser: "@typescript-eslint/parser",
        sourceType: "module",
      },
      plugins: ["vue""@typescript-eslint"],
      rules: {},
    };

    根據項目實際情況,如果我們有額外的 ESLint 規則,也在此文件中追加。

注意:

配置好以後,我們在 VSCode 或 WebStorm 等編輯器中開啓 ESLin,寫代碼時,ESLint 就會按照我們配置的規則來進行實時代碼檢查,發現問題會給出對應錯誤提示和修復方案。

如圖:

雖然,現在編輯器已經給出錯誤提示和修復方案,但需要我們一個一個去點擊修復,還是挺麻煩的。很簡單,我們只需設置編輯器保存文件時自動執行 eslint --fix 命令進行代碼風格修復。

解決 Prettier 和 ESLint 的衝突

通常大家會在項目中根據實際情況添加一些額外的 ESLint 和 Prettier 配置規則,難免會存在規則衝突情況。

本項目中的 ESLint 配置中使用了 Airbnb JavaScript 風格指南校驗,其規則之一是_代碼結束後面要加分號_,而我們在 Prettier 配置文件中加了_代碼結束後面不加分號_的配置項,這樣就有衝突了,會出現用 Prettier 格式化後的代碼,ESLint 檢測到格式有問題的,從而拋出錯誤提示。

解決兩者衝突問題,需要用到 eslint-plugin-prettiereslint-config-prettier

最後形成優先級:Prettier 配置規則 > ESLint 配置規則

這樣,我們在執行 eslint --fix 命令時,ESLint 就會按照 Prettier 的配置規則來格式化代碼,輕鬆解決二者衝突問題。

集成 husky 和 lint-staged

我們在項目中已集成 ESLint 和 Prettier,在編碼時,這些工具可以對我們寫的代碼進行實時校驗,在一定程度上能有效規範我們寫的代碼,但團隊可能會有些人覺得這些條條框框的限制很麻煩,選擇視 “提示” 而不見,依舊按自己的一套風格來寫代碼,或者乾脆禁用掉這些工具,開發完成就直接把代碼提交到了倉庫,日積月累,ESLint 也就形同虛設。

所以,我們還需要做一些限制,讓沒通過 ESLint 檢測和修復的代碼禁止提交,從而保證倉庫代碼都是符合規範的。

爲了解決這個問題,我們需要用到 Git Hook,在本地執行 git commit 的時候,就對所提交的代碼進行 ESLint 檢測和修復(即執行 eslint --fix),如果這些代碼沒通過 ESLint 規則校驗,則禁止提交。

實現這一功能,我們藉助 husky[35] + lint-staged[36] 。

husky[37] —— Git Hook 工具,可以設置在 git 各個階段(pre-commitcommit-msgpre-push 等)觸發我們的命令。
lint-staged[38] —— 在 git 暫存的文件上運行 linters。

配置 husky

  1. 安裝 husky 到開發依賴

  2. 在項目根目錄下創建 .husky 目錄

  3. .husky 目錄創建 pre-commit hook,並初始化 pre-commit 命令爲 npm test

  4. 修改 package.jsonscripts,增加 "prepare": "husky install"

  1. 安裝 husky

    npm i husky -D
  2. 創建 Git hooks

    npx husky install

    該命令做了兩件事:

  3. 手動修改 package.jsonscripts,增加 "prepare": "husky install"

特別注意:本項目使用 husky 6.x 版本,6.x 版本配置方式跟之前的版本有較大差異。目前網上大部分有關 husky 的教程都是 6 以前的版本 ,跟本文教程不太一樣,當發現配置方法不一致時,一切以 husky 官網 [39] 爲準。

到這裏,husky 配置完畢,現在我們來使用它:

husky 包含很多 hook(鉤子),常用有:pre-commitcommit-msgpre-push。這裏,我們使用 pre-commit 來觸發 ESLint 命令。

修改 .husky/pre-commit hook 文件的觸發命令:

eslint --fix ./src --ext .vue,.js,.ts

image

上面這個 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 add那些文件(即 git 暫存區的文件),而不會影響到其他文件。

接下來,我們使用 lint-staged 繼續優化項目。

  1. 安裝 lint-staged

    npm i lint-staged -D
  2. package.json裏增加 lint-staged 配置項

    image

    "lint-staged"{
      "*.{vue,js,ts}""eslint --fix"
    },

    這行命令表示:只對 git 暫存區的 .vue.js.ts 文件執行 eslint --fix

  3. 修改 .husky/pre-commit hook 的觸發命令爲:npx lint-staged

    image

至此,husky 和 lint-staged 組合配置完成。

現在我們提交代碼時就會變成這樣:

假如我們修改了 scr 目錄下的 test-1.jstest-2.tstest-3.md 文件,然後 git add ./src/,最後 git commit -m "test...",這時候就會只對 test-1.jstest-2.ts 這兩個文件執行 eslint --fix。如果 ESLint 通過,成功提交,否則終止提交。從而保證了我們提交到 Git 倉庫的代碼都是規範的。

image

無論寫代碼還是做其他事情,都應該用長遠的眼光來看,剛開始使用 ESint 的時候可能會有很多問題,改起來也很費時費力,只要堅持下去,代碼質量和開發效率都會得到提升,前期的付出都是值得的。

這些工具並不是必須的,沒有它們你同樣可以可以完成功能開發,但是利用好這些工具,你可以寫出更高質量的代碼。特別是一些剛剛接觸的人,可能會覺得麻煩而放棄使用這些工具,失去了一次提升編程能力的好機會。

提交規範

前面我們已經統一代碼規範,並且在提交代碼時進行強約束來保證倉庫代碼質量。多人協作的項目中,在提交代碼這個環節,也存在一種情況:不能保證每個人對提交信息的準確描述,因此會出現提交信息紊亂、風格不一致的情況。

如果 git commit 的描述信息精準,在後期維護和 Bug 處理時會變得有據可查,項目開發週期內還可以根據規範的提交信息快速生成開發日誌,從而方便我們追蹤項目和把控進度。

這裏,我們使用社區最流行、最知名、最受認可的 Angular 團隊提交規範。

先看看 Angular 項目的提交記錄 [40]:

image

如上圖,可以看出這些提交信息都是有固定格式的,下面我們來學習 Angular 規範的 commit message 格式。

commit message 格式規範

commit message 由 Header、Body、Footer 組成。

<Header>

<Body>

<Footer>

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

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

type 用於說明 commit 的提交類型(必須是以下幾種之一)。

gi0H5M

scope

scope 用於指定本次 commit 影響的範圍。scope 依據項目而定,例如在業務項目中可以依據菜單或者功能模塊劃分,如果是組件庫開發,則可以依據組件劃分。(scope 可省略)

subject

subject 是本次 commit 的簡潔描述,長度約定在 50 個字符以內,通常遵循以下幾個規範:

Body

body 是對本次 commit 的詳細描述,可以分成多行。(body 可省略)

跟 subject 類似,用動詞開頭,body 應該說明修改的原因和更改前後的行爲對比。

如果本次提交的代碼是突破性的變更或關閉缺陷,則 Footer 必需,否則可以省略。

參考例子

規範 commit message 的好處

集成 Commitizen 實現規範提交

上面介紹了 Angular 規範提交的格式,初次接觸的同學咋一看可能會覺得複雜,其實不然,如果讓大家在 git commit 的時候嚴格按照上面的格式來寫,肯定是有壓力的,首先得記住不同的類型到底是用來定義什麼,subject 怎麼寫,body 怎麼寫,footer 要不要寫等等問題,懶纔是程序員第一生產力,爲此我們使用 Commitizen 工具來幫助我們自動生成 commit message 格式,從而實現規範提交。

Commitizen 是一個幫助撰寫規範 commit message 的工具。它有一個命令行工具 cz-cli。

安裝 Commitizen

npm install commitizen -D

初始化項目

成功安裝 Commitizen 後,我們用 cz-conventional-changelog 適配器來初始化項目:

npx commitizen init cz-conventional-changelog --save-dev --save-exact

這行命令做了兩件事:

使用 Commitizen

以前我們提交代碼都是 git commit -m "xxx",現在改爲 git cz,然後按照終端操作提示,逐步填入信息,就能自動生成規範的 commit message。

最後,在 Git 提交歷史中就能看到剛剛規範的提交記錄了:

自定義配置提交說明

從上面的截圖可以看到,git cz 終端操作提示都是英文的,如果想改成中文的或者自定義這些配置選項,我們使用 cz-customizable 適配器。

cz-customizable 初始化項目

運行如下命令使用 cz-customizable 初始化項目,注意之前已經初始化過一次,這次再初始化,需要加 --force 覆蓋。

npx commitizen init cz-customizable --save-dev --save-exact --force

這行命令做了兩件事:

使用 cz-customizable

在項目根目錄下創建 .cz-config.js 文件,然後按照官方提供的示例 [41] 來配置。

在本項目中我們修改成中文:

module.exports = {
  // type 類型(定義之後,可通過上下鍵選擇)
  types: [
    { value: 'feat', name: 'feat:     新增功能' },
    { value: 'fix', name: 'fix:      修復 bug' },
    { value: 'docs', name: 'docs:     文檔變更' },
    { value: 'style', name: 'style:    代碼格式(不影響功能,例如空格、分號等格式修正)' },
    { value: 'refactor', name: 'refactor: 代碼重構(不包括 bug 修復、功能新增)' },
    { value: 'perf', name: 'perf:     性能優化' },
    { value: 'test', name: 'test:     添加、修改測試用例' },
    { value: 'build', name: 'build:    構建流程、外部依賴變更(如升級 npm 包、修改 webpack 配置等)' },
    { value: 'ci', name: 'ci:       修改 CI 配置、腳本' },
    { value: 'chore', name: 'chore:    對構建過程或輔助工具和庫的更改(不影響源文件、測試用例)' },
    { value: 'revert', name: 'revert:   回滾 commit' }
  ],

  // scope 類型(定義之後,可通過上下鍵選擇)
  scopes: [
    ['components''組件相關'],
    ['hooks''hook 相關'],
    ['utils''utils 相關'],
    ['element-ui''對 element-ui 的調整'],
    ['styles''樣式相關'],
    ['deps''項目依賴'],
    ['auth''對 auth 修改'],
    ['other''其他修改'],
    // 如果選擇 custom,後面會讓你再輸入一個自定義的 scope。也可以不設置此項,把後面的 allowCustomScopes 設置爲 true
    ['custom''以上都不是?我要自定義']
  ].map(([value, description]) ={
    return {
      value,
      name: `${value.padEnd(30)} (${description})`
    }
  }),

  // 是否允許自定義填寫 scope,在 scope 選擇的時候,會有 empty 和 custom 可以選擇。
  // allowCustomScopes: true,

  // allowTicketNumber: false,
  // isTicketNumberRequired: false,
  // ticketNumberPrefix: 'TICKET-',
  // ticketNumberRegExp: '\\d{1,5}',


  // 針對每一個 type 去定義對應的 scopes,例如 fix
  /*
  scopeOverrides: {
    fix: [
      { name: 'merge' },
      { name: 'style' },
      { name: 'e2eTest' },
      { name: 'unitTest' }
    ]
  },
  */

  // 交互提示信息
  messages: {
    type: '確保本次提交遵循 Angular 規範!\n選擇你要提交的類型:',
    scope: '\n選擇一個 scope(可選):',
    // 選擇 scope: custom 時會出下面的提示
    customScope: '請輸入自定義的 scope:',
    subject: '填寫簡短精煉的變更描述:\n',
    body:
      '填寫更加詳細的變更描述(可選)。使用 "|" 換行:\n',
    breaking: '列舉非兼容性重大的變更(可選):\n',
    footer: '列舉出所有變更的 ISSUES CLOSED(可選)。 例如: #31, #34:\n',
    confirmCommit: '確認提交?'
  },

  // 設置只有 type 選擇了 feat 或 fix,才詢問 breaking message
  allowBreakingChanges: ['feat''fix'],

  // 跳過要詢問的步驟
  // skipQuestions: ['body''footer'],

  // subject 限制長度
  subjectLimit: 100
  breaklineChar: '|', // 支持 body 和 footer
  // footerPrefix : 'ISSUES CLOSED:'
  // askForBreakingChangeFirst : true,
}

建議大家結合項目實際情況來自定義配置提交規則,例如很多時候我們不需要寫長描述,公司內部的代碼倉庫也不需要管理 issue,那麼可以把詢問 body 和 footer 的步驟跳過(在 .cz-config.js 中修改成 skipQuestions: ['body', 'footer'])。

image

集成 commitlint 驗證提交規範

在 “代碼規範” 章節,我們已經講到過,儘管制定了規範,但在多人協作的項目中,總有些人依舊我行我素,因此提交代碼這個環節,我們也增加一個限制:只讓符合 Angular 規範的 commit message 通過,我們藉助 @commitlint/config-conventional 和 @commitlint/cli 來實現。

安裝 commitlint

安裝 @commitlint/config-conventional 和 @commitlint/cli

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

配置 commitlint

commitlint 驗證

因爲已在項目中集成 commitizen,建議大家用 git cz 來代替 git commit 提交代碼,可以保證提交信息規範。

單元測試

單元測試是項目開發中一個非常重要的環節,完整的測試能爲代碼和業務提供質量保證,減少 Bug 的出現。

本章節將帶領大家在 Vite + Vue3 + TypeScript 的項目中集成單元測試工具。

安裝核心依賴

我們使用 Vue 官方提供的 vue-test-utils 和社區流行的測試工具 jest 來進行 Vue 組件的單元測試。

安裝這些工具爲開發依賴(devDependencies):

npm i @vue/test-utils@next jest vue-jest@next ts-jest -D

創建 jest 配置文件

在項目根目錄下新建 jest.config.js 文件:

module.exports = {
  moduleFileExtensions: ["vue""js""ts"],
  preset: "ts-jest",
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.vue$""vue-jest", // vue 文件用 vue-jest 轉換
    "^.+\\.ts$""ts-jest", // ts 文件用 ts-jest 轉換
  },
  // 匹配 __tests__ 目錄下的 .js/.ts 文件 或其他目錄下的 xx.test.js/ts xx.spec.js/ts
  testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(ts)$",
};

創建單元測試文件

在上面的 jest.config.js 文件中,我們配置只匹配 __tests__ 目錄下的任意 .ts 文件或其他目錄下的 xx.test.ts/xx.spec.ts 文件進行單元測試。

這裏,我們在項目根目錄下創建 tests 目錄來存儲單元測試文件

├── src/
└── tests/                           // 單元測試目錄
    ├── Test.spec.ts                 // Test 組件測試
<template>
  <div class="test-container page-container">
    <div class="page-title">Unit Test Page</div>
    <p>count is: {{ count }}</p>
    <button @click="increment">increment</button>
  </div>
</template>

<script lang="ts">
  import { defineComponent, ref } from "vue";

  export default defineComponent({
    name: "Vuex",
    setup() {
      const count = ref<number>(0);
      const increment = () ={
        count.value += 1;
      };
      return { count, increment };
    },
  });
</script>

集成 @types/jest

image

如上圖,我們使用 VSCode / WebStrom / IDEA 等編輯器時,在單元測試文件中,IDE 會提示某些方法不存在(如 testdescribeitexpect等),安裝 @types/jest 即可解決。

npm i @types/jest -D

TypeScript 的編譯器也會提示 jest 的方法和類型找不到,我們還需把 @types/jest 添加根目錄下的 ts.config.json(TypeScript 配置文件)中:

{
  "compilerOptions"{
    ...
    "types"["vite/client""jest"]
  },
}

添加 eslint-plugin-jest

image

因爲我們在項目中集成了 ESLint,如上圖很明顯是沒通過 ESLint 規則檢驗。因此,我們還需要在 ESLint 中增加 eslint-plugin-jest 插件來解除對 jest 的校驗。

現在,我們的單元測試代碼就不會有錯誤提示信息了 ؏؏☝ᖗ 乛 ◡ 乛 ᖘ☝؏؏

image

執行單元測試

在根目錄下 package.json 文件的 scripts 中,添加一條單元測試命令:"test": "jest"

image

執行命令 npm run test 即可進行單元測試,jest 會根據 jest.config.js 配置文件去查找 __tests__ 目錄下的 .ts 文件或其他任意目錄下的 .spec.ts.test.ts 文件,然後執行單元測試方法。

你可以在 jest.config.js 配置文件中,自由配置單元測試文件的目錄。

當單元測試沒有全部通過時,我們需要根據報錯信息去優化對應組件的代碼,進一步提高項目健壯性。但是寫單元測試是件比較痛苦的事,我個人覺得也沒必要全部組件都寫單元測試,根據項目實際情況有針對性去寫就行了。

單元測試約束

前面,我們使用 husky 在 Git 的 pre-commitcommit-msg 階段分別約束代碼風格規範和提交信息規範。這一步,我們在 pre-push 階段進行單元測試,只有單元測試全部通過才讓代碼 push 到遠端倉庫,否則終止 push

使用 husky 命令在 .husky 目錄下自動創建 pre-push hook 文件,並在此執行單元測試命令 npm run test

npx husky add .husky/pre-push "npm run test $1"

image

現在,我們在 git push 時就能先進行單元測試了,只有單元測試全部通過,才能成功 push

自動部署

到了這一步,我們已經在項目中集成代碼規範約束提交信息規範約束單元測試約束,從而保證我們遠端倉庫(如 GitHub、GitLab、Gitee 倉庫等)的代碼都是高質量的。

本項目是要搭建一套規範的前端工程化環境,爲此我們使用 CI(Continuous Integration 持續集成)來完成項目最後的部署工作。

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

這裏,我們使用 GitHub Actions。

什麼是 GitHub Actions

GitHub Actions 是 GitHub 的持續集成服務,持續集成由很多操作組成,比如抓取代碼、運行測試、登錄遠程服務器、發佈到第三方服務等等,GitHub 把這些操作稱爲 actions。

配置 GitHub Actions

創建 GitHub 倉庫

因爲 GitHub Actions 只對 GitHub 倉庫有效,所以我們創建 GitHub 倉庫 [46] 來託管項目代碼。

image

其中,我們用:

gh-pages 分支,是 GitHub Pages 服務的固定分支,可以通過 HTTP 的方式訪問到這個分支的靜態文件資源。

創建 GitHub Token

創建一個有 repoworkflow 權限的 GitHub Token[47]

image

注意:新生成的 Token 只會顯示一次,保存起來,後面要用到。如有遺失,重新生成即可。

image

在倉庫中添加 secret

將上面新創建的 Token 添加到 GitHub 倉庫的 Secrets 裏,並將這個新增的 secret 命名爲 VUE3_DEPLOY (名字無所謂,看你喜歡)。

步驟:倉庫 -> settings -> Secrets -> New repository secret

image

新創建的 secret VUE3_DEPLOY 在 Actions 配置文件中要用到,兩個地方需保持一致!

創建 Actions 配置文件

  1. 在項目根目錄下創建 .github 目錄。

  2. .github 目錄下創建 workflows 目錄。

  3. workflows 目錄下創建 deploy.yml 文件。

image

deploy.yml 文件的內容:

name: deploy

on:
  push:
    branches: [master] # master 分支有 push 時觸發

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Setup Node.js v14.x
        uses: actions/setup-node@v1
        with:
          node-version: "14.x"

      - name: Install
        run: npm install # 安裝依賴

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

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3 # 使用部署到 GitHub pages 的 action
        with:
          publish_dir: ./dist # 部署打包後的 dist 目錄
          github_token: ${{ secrets.VUE3_DEPLOY }} # secret 名
          user_name: ${{ secrets.MY_USER_NAME }}
          user_email: ${{ secrets.MY_USER_EMAIL }}
          commit_message: Update Vite2.x + Vue3.x + TypeScript Starter # 部署時的 git 提交信息,自由填寫

自動部署觸發原理

當有新提交的代碼 push 到 GitHub 倉庫時,就會觸發 GitHub Actions,在 GitHub 服務器上執行 Action 配置文件裏面的命令,例如:安裝依賴項目打包等,然後將打包好的靜態文件部署到 GitHub Pages 上,最後,我們就能通過域名訪問了。

🌏 通過域名 https://vite-vue3-starter.xpoet.cn/ 訪問本項目

使用自動部署,我們只需專注於項目開發階段,任何重複且枯燥的行爲都交由程序去完成,懶纔是程序員第一生產力。

事實上,自動部署只是 GitHub Actions 功能的冰山一角,GitHub Actions 能做的事還很多很多,大家感興趣的話自行查閱。

最後

本文從技術選項到架構搭建、從代碼規範約束到提交信息規範約束,從單元測試到自動部署,一步一步帶領大家如何從一個最簡單的前端項目骨架到規範的前端工程化環境,基本上涵蓋了前端項目開發的整個週期。

因篇幅較長,所涉及技術點較多,難免會出現錯誤,希望大家多多指正,謝謝大家!

本項目完整的代碼託管在 GitHub 倉庫 [48],需要的同學自取,別忘了點 Star 支持作者~

參考資料

[1]

GitHub 倉庫: https://github.com/XPoet/vite-vue3-starter

[2]

TypeScript 4.x: https://www.typescriptlang.org/zh/

[3]

JavaScript: https://www.javascript.com/

[4]

Vite 2.x: https://cn.vitejs.dev/

[5]

Vue 3.x: https://v3.cn.vuejs.org/

[6]

Vue Router 4.x: https://next.router.vuejs.org/zh/index.html

[7]

Vuex 4.x: https://next.vuex.vuejs.org/

[8]

Element Plus: https://element-plus.org/#/zh-CN

[9]

Stylus: https://stylus-lang.com/

[10]

Sass: https://sass.bootcss.com/documentation

[11]

Less: http://lesscss.cn/

[12]

Axios: https://axios-http.com/

[13]

husky: https://typicode.github.io/husky/#/

[14]

lint-staged: https://github.com/okonet/lint-staged

[15]

EditorConfig: http://editorconfig.org

[16]

Prettier: https://prettier.io/

[17]

ESLint: https://eslint.org/

[18]

Airbnb JavaScript Style Guide: https://github.com/airbnb/javascript#translation

[19]

Commitizen: http://commitizen.github.io/cz-cli/

[20]

Commitlint: https://commitlint.js.org/#/

[21]

vue-test-utils: https://next.vue-test-utils.vuejs.org/

[22]

jest: https://jestjs.io/

[23]

vue-jest: https://github.com/vuejs/vue-jest

[24]

ts-jest: https://kulshekhar.github.io/ts-jest/

[25]

GitHub Actions: https://docs.github.com/cn/actions/learn-github-actions

[26]

GitHub 倉庫: https://github.com/XPoet/vite-vue3-starter

[27]

配置文件: https://prettier.io/docs/en/configuration.html

[28]

Prettier-Options: https://prettier.io/docs/en/options.html

[29]

ESLint: https://github.com/eslint/eslint

[30]

Airbnb JavaScript Style Guide: https://github.com/airbnb/javascript

[31]

Airbnb JavaScript 風格指南 - 中文版: https://github.com/lin-123/javascript

[32]

JavaScript Standard Style: https://github.com/standard/standard

[33]

JavaScript Standard Style - 中文版: https://github.com/standard/standard/blob/master/docs/README-zhcn.md

[34]

Google JavaScript Style Guide: https://google.github.io/styleguide/jsguide.html

[35]

husky: https://github.com/typicode/husky

[36]

lint-staged: https://github.com/okonet/lint-staged

[37]

husky: https://github.com/typicode/husky

[38]

lint-staged: https://github.com/okonet/lint-staged

[39]

husky 官網: https://typicode.github.io/husky/#/?id=usage

[40]

Angular 項目的提交記錄: https://github.com/angular/angular/commits/master

[41]

示例: https://github.com/leoforfree/cz-customizable/blob/master/cz-config-EXAMPLE.js

[42]

vue-test-utils: https://github.com/vuejs/vue-test-utils-next

[43]

jest: https://github.com/facebook/jest

[44]

vue-jest: https://github.com/vuejs/vue-jest

[45]

ts-jest: https://github.com/kulshekhar/ts-jest

[46]

創建 GitHub 倉庫: https://github.com/new

[47]

GitHub Token: https://github.com/settings/tokens/new

[48]

GitHub 倉庫: https://github.com/XPoet/vite-vue3-starter

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/uLliWY6-PGNA9jog4myEkg