打造 vue3 項目模板

環境依賴版本

node:v14.15.4
vite:^2.8.0
vue:^3.2.25
typescript:^4.5.4
pinia:^2.0.12
vue-router:^4.0.14
vueuse:^8.2.0
eslint:^8.12.0
prettier:^2.6.1
commitizen:^4.2.4
husky:^7.0.4

長話短說,直接開幹 ~

初始化項目

  1. 按步驟提示初始化:

#使用 vite-cli 命令
# pnpm
pnpm create vite
# npm
npm init vite@latest
# yarn
yarn create vite
  1. 輸入項目名:
? Project name:  vite-vue3-ts-pinia
  1. 選擇一個框架(vue)
? Select a framework: » - Use arrow-keys. Return to submit.
     vanilla // 原生js
 >   vue     // 默認就是 vue3
     react   // react
     preact  // 輕量化react框架
     lit     // 輕量級web組件
     svelte  // svelte框架
  1. 使用 typescript
? Select a variant: › - Use arrow-keys. Return to submit.
     vue
 ❯   vue-ts
  1. 啓動項目
cd vite-vue3-ts-pinia && pnpm install && pnpm run dev

快速初始化(建議使用):

# pnpm
pnpm create vite project-name -- --template vue-ts
# npm 6.x
npm init vite@latest project-name --template vue-ts
# npm 7+, 需要額外的雙橫線:
npm init vite@latest project-name -- --template vue-ts
# yarn
yarn create vite project-name --template vue-ts

集成配置

  1. 爲保證 node 的使用
pnpm i @types/node --save-dev
  1. 修改 tsconfig.json
{
  "compilerOptions": {
    "typeRoots": [
      "node_modules/@types", // 默認值
      "src/types"
   ],
    "target": "esnext",
    "useDefineForClassFields": true,
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "baseUrl": "./",
    "paths":{
      "@": ["src"],
      "@/*": ["src/*"],
    }
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
  1. 修改 vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import * as path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
    resolve: {
        //設置別名
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    },
    plugins: [vue()],
    server: {
        port: 8080, //啓動端口
        hmr: {
            host: '127.0.0.1',
            port: 8080
        },
        // 設置 https 代理
        proxy: {
            '/api': {
                target: 'your https address',
                changeOrigin: true,
                rewrite: (path: string) => path.replace(/^\/api/, '')
            }
        }
    }
});

2. 代碼質量風格的統一

集成 eslint

  1. 安裝
pnpm i eslint eslint-plugin-vue --save-dev

由於 ESLint 默認使用 Espree 進行語法解析,無法識別 TypeScript 的一些語法,故我們需要安裝 @typescript-eslint/parser 替代掉默認的解析器

pnpm install @typescript-eslint/parser --save-dev

安裝對應的插件 @typescript-eslint/eslint-plugin 它作爲 eslint 默認規則的補充,提供了一些額外的適用於 ts 語法的規則。

pnpm install @typescript-eslint/eslint-plugin --save-dev
  1. 創建配置文件:.eslintrc.js 或 .eslintrc.json
module.exports = {
    parser: 'vue-eslint-parser',
    parserOptions: {
        parser: '@typescript-eslint/parser',
        ecmaVersion: 2020,
        sourceType: 'module',
        ecmaFeatures: {
            jsx: true
        }
    },
    extends: [
        'plugin:vue/vue3-recommended',
        'plugin:@typescript-eslint/recommended',
    ],
    rules: {
        // override/add rules settings here, such as:
    }
};
  1. 創建忽略文件:.eslintignore
node_modules/
dist/
index.html
  1. 命令行式運行:修改 package.json
{
    ...
    "scripts": {
        ...
        "eslint:comment": "使用 ESLint 檢查並自動修復 src 目錄下所有擴展名爲 .js 和 .vue 的文件",
        "eslint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
    }
    ...
}

集成 prettier

  1. 安裝
pnpm i prettier eslint-config-prettier eslint-plugin-prettier --save-dev
  1. 創建配置文件:prettier.config.js.prettierrc.js
module.exports = {
    // 一行最多 80 字符
    printWidth: 80,
    // 使用 4 個空格縮進
    tabWidth: 4,
    // 不使用 tab 縮進,而使用空格
    useTabs: false,
    // 行尾需要有分號
    semi: true,
    // 使用單引號代替雙引號
    singleQuote: true,
    // 對象的 key 僅在必要時用引號
    quoteProps: 'as-needed',
    // jsx 不使用單引號,而使用雙引號
    jsxSingleQuote: false,
    // 末尾使用逗號
    trailingComma: 'all',
    // 大括號內的首尾需要空格 { foo: bar }
    bracketSpacing: true,
    // jsx 標籤的反尖括號需要換行
    jsxBracketSameLine: false,
    // 箭頭函數,只有一個參數的時候,也需要括號
    arrowParens: 'always',
    // 每個文件格式化的範圍是文件的全部內容
    rangeStart: 0,
    rangeEnd: Infinity,
    // 不需要寫文件開頭的 @prettier
    requirePragma: false,
    // 不需要自動在文件開頭插入 @prettier
    insertPragma: false,
    // 使用默認的折行標準
    proseWrap: 'preserve',
    // 根據顯示樣式決定 html 要不要折行
    htmlWhitespaceSensitivity: 'css',
    // 換行符使用 lf
    endOfLine: 'auto'
}
  1. 修改 .eslintrc.js 配置
module.exports = {
    ...
    extends: [
        'plugin:vue/vue3-recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier',
        'plugin:prettier/recommended'
    ],
    ...
};
  1. 命令行式運行:修改 package.json
{
    ...
    "scripts": {
        ...
        "prettier:comment": "自動格式化當前目錄下的所有文件",
        "prettier": "prettier --write"
    }
    ...
}

3. 集成 pinia

Pinia 讀音:['piːnə],是 Vue 官方團隊推薦代替Vuex的一款輕量級狀態管理庫。

Pinia 有如下特點:

安裝

pnpm i pinia --save

使用

  1. 新建 src/store 目錄並在其下面創建 index.ts,導出 store
import { createPinia } from 'pinia'
const store = createPinia()
export default store
  1. 在 main.ts 中引入並使用
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
// 創建vue實例
const app = createApp(App)
// 掛載pinia
app.use(store)
// 掛載實例
app.mount('#app');
  1. 定義 State: 在 src/store 下面創建一個 user.ts
import { defineStore } from 'pinia'
export const useUserStore = defineStore({
   id: 'user', // id必填,且需要唯一
   state: () => {
     return {
       name: '張三'
     }
   },
   actions: {
     updateName(name) {
       this.name = name
     }
   }
})
  1. 獲取 State: 在 src/components/usePinia.vue 中使用
<template>
   <div>{{ userStore.name }}</div>
</template>
<script lang="ts" setup>
 import { useUserStore } from '@/store/user'
 const userStore = useUserStore()
</script>
  1. 修改 State:
// 1. 直接修改 state (不建議)
userStore.name = '李四'
// 2. 通過 actions 去修改
<script lang="ts" setup>
import { useUserStore } from '@/store/user'
const userStore = useUserStore()
userStore.updateName('李四')
</script>

4. 集成 vue-router4

安裝

pnpm i vue-router --save

使用

  1. 新建 src/router 目錄並在其下面創建 index.ts,導出 router
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const routes: Array<RouteRecordRaw> = [
   {
     path: '/login',
     name: 'Login',
     meta: {
         title: '登錄',
         keepAlive: true,
         requireAuth: false
     },
     component: () => import('@/pages/login.vue')
   },
   {
       path: '/',
       name: 'Index',
       meta: {
           title: '首頁',
           keepAlive: true,
           requireAuth: true
       },
       component: () => import('@/pages/index.vue')
   }
]
const router = createRouter({
   history: createWebHistory(),
   routes
});
export default router;
  1. 在 main.ts 中引入並使用
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
import router from '@/router';
// 創建vue實例
const app = createApp(App);
app.use(router);
// 掛載實例
app.mount('#app');
  1. 修改 App.vue
<template>
   <RouterView/>
</template>

5. 集成 vueuse

VueUse 是一個基於 Composition API 的實用函數集合。

安裝

pnpm i @vueuse/core

使用

  1. 創建一個新的 src/page/vueUse.vue 頁面來做一個簡單的 demo
<template>
   <h1> 測試 vueUse 的鼠標座標 </h1>
   <h3>Mouse: {{x}} x {{y}}</h3>
</template>
<script lang="ts">
   import { defineComponent } from 'vue';
   import { useMouse } from '@vueuse/core'
   export default defineComponent({
       name: 'VueUse',
       setup() {
         const { x, y } = useMouse()
         return {
           x, y
         }
       }
   });
</script>

useMouse 只是 vueuse 的一個最基本的函數庫,還有許多,總會有一個適合你;

6. CSS 的集成

方案一:原生 css variable 新特性:

原生支持,不需要第三方插件,具體使用文檔可 查看

  1. 新建文件 src/styles/index.css
:root {
   --main-bg-color: pink;
 }
 body {
   background-color: var(--main-bg-color);
 }

注:還可以增加 PostCSS 配置,(任何受 postcss-load-config 支持的格式,例如 postcss.config.js ),它將會自動應用於所有已導入的 CSS。

方案二:scss 或 less:

  1. 安裝
# .scss and .sass
pnpm add -D sass
# .less
pnpm add -D less
  1. 使用在 .vue 文件模板中
// .scss
<template>
   <div>
       <h3>歡迎使用 scss</h3>
   </div>
</template>
<style lang="scss">
 .root {}
</style>
// .less
<template>
   <div>
       <h3>歡迎使用 less</h3>
   </div>
</template>
<style lang="less">
 .root {}
</style>

7. 集成 axios

axios 是一個基於 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。

安裝

pnpm i axios

使用:

  1. 新建 src/utils/axios.ts
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios';
const service = axios.create();
 // Request interceptors
service.interceptors.request.use(
     (config: AxiosRequestConfig) => {
         // do something
         return config;
     },
     (error: any) => {
         Promise.reject(error);
     }
);
 // Response interceptors
service.interceptors.response.use(
     async (response: AxiosResponse) => {
         // do something
     },
     (error: any) => {
         // do something
         return Promise.reject(error);
     }
);
export default service;
  1. 在頁面中使用即可
<script lang="ts">
  import request from '@/utils/axios';
  const requestRes = async () => {
      let result = await request({
                  url: '/api/xxx',
                  method: 'get'
                });
    }
 </script>

封裝請求參數和響應數據的所有 api (可選項)

  1. 新建 src/api/index.ts
import * as login from './module/login';
import * as index from './module/index';
export default Object.assign({}, login, index);
  1. 新建 src/api/module/login.tssrc/api/module/index.ts
import request from '@/utils/axios';
/**
 * 登錄
 */
interface IResponseType<P = {}> {
    code?: number;
    status: number;
    msg: string;
    data: P;
}
interface ILogin {
    token: string;
    expires: number;
}
export const login = (username: string, password: string) => {
    return request<IResponseType<ILogin>>({
        url: '/api/auth/login',
        method: 'post',
        data: {
            username,
            password
        }
    });
};
  1. 由於使用了 typescript,所以需新增 src/types/shims-axios.d.ts
import { AxiosRequestConfig } from 'axios';
/**
 * 自定義擴展axios模塊
 * @author Maybe
 */
declare module 'axios' {
    export interface AxiosInstance {
        <T = any>(config: AxiosRequestConfig): Promise<T>;
        request<T = any>(config: AxiosRequestConfig): Promise<T>;
        get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
        delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
        head<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
        post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
        put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
        patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
    }
}
  1. src/pages/request.vue 頁面中使用
<script lang="ts">
  import API from '@/api';
  const requestRes = async () => {
      let result = await API.login('zhangsan', '123456');
  }
</script>

8. css 的 UI 樣式庫

可選很多,根據自己項目的需求去進行選擇即可

注意:UI 庫一般需要按需引入(下面以 element-plus 爲例)

  1. 安裝 vite-plugin-style-import
pnpm i vite-plugin-style-import --save-dev
  1. 修改 vite.config.ts
...
import {
  createStyleImportPlugin,
  ElementPlusResolve,
} from 'vite-plugin-style-import'
export default defineConfig({
    ...
    plugins: [
        vue(),
        createStyleImportPlugin({
            resolves:[
                ElementPlusResolve()
            ],
            libs: [
                // 如果沒有你需要的resolve,可以在lib內直接寫,也可以給我們提供PR
                {
                    libraryName: 'element-plus',
                    esModule: true,
                    resolveStyle: (name) => {
                        return `element-plus/lib/theme-chalk/${name}.css`
                    },
                    ensureStyleFile: true // 忽略文件是否存在, 導入不存在的CSS文件時防止錯誤。
                },
            ],
        })
    ],
    ...
})
  1. 亦或者使用 unplugin-vue-components 來進行按需加載 vue 組件

9. 使用 commitizen 規範 git 提交

爲了使團隊多人協作更加的規範,所以需要每次在 git 提交的時候,做一次硬性規範提交,規範 git 的提交信息

安裝 commitizen (交互式提交 + 自定義提示文案 + Commit 規範)

  1. 安裝
pnpm install -D commitizen cz-conventional-changelog @commitlint/config-conventional @commitlint/cli commitlint-config-cz cz-customizable
  1. 配置 package.json
{
  ...
  "scripts": {
    "commit:comment": "引導設置規範化的提交信息",
    "commit":"git-cz",
  },
  "config": {
      "commitizen": {
        "path": "node_modules/cz-customizable"
      }
  },
  ...
}
  1. 新增配置 commitlint.config.js
module.exports = {
    extends: ['@commitlint/config-conventional', 'cz'],
    rules: {
        'type-enum': [
            2,
            'always',
            [
                'feature', // 新功能(feature)
                'bug', // 此項特別針對bug號,用於向測試反饋bug列表的bug修改情況
                'fix', // 修補bug
                'ui', // 更新 ui
                'docs', // 文檔(documentation)
                'style', // 格式(不影響代碼運行的變動)
                'perf', // 性能優化
                'release', // 發佈
                'deploy', // 部署
                'refactor', // 重構(即不是新增功能,也不是修改bug的代碼變動)
                'test', // 增加測試
                'chore', // 構建過程或輔助工具的變動
                'revert', // feat(pencil): add ‘graphiteWidth’ option (撤銷之前的commit)
                'merge', // 合併分支, 例如:merge(前端頁面):feature-xxxx修改線程地址
                'build', // 打包
            ],
        ],
        // <type> 格式 小寫
        'type-case': [2, 'always', 'lower-case'],
        // <type> 不能爲空
        'type-empty': [2, 'never'],
        // <scope> 範圍不能爲空
        'scope-empty': [2, 'never'],
        // <scope> 範圍格式
        'scope-case': [0],
        // <subject> 主要 message 不能爲空
        'subject-empty': [2, 'never'],
        // <subject> 以什麼爲結束標誌,禁用
        'subject-full-stop': [0, 'never'],
        // <subject> 格式,禁用
        'subject-case': [0, 'never'],
        // <body> 以空行開頭
        'body-leading-blank': [1, 'always'],
        'header-max-length': [0, 'always', 72],
    },
};
  1. 自定義提示則添加 .cz-config.js
module.exports = {
    types: [
        {value: 'feature',  name: 'feature:  增加新功能'},
        {value: 'bug',      name: 'bug:      測試反饋bug列表中的bug號'},
        {value: 'fix',      name: 'fix:      修復bug'},
        {value: 'ui',       name: 'ui:       更新UI'},
        {value: 'docs',     name: 'docs:     文檔變更'},
        {value: 'style',    name: 'style:    代碼格式(不影響代碼運行的變動)'},
        {value: 'perf',     name: 'perf:     性能優化'},
        {value: 'refactor', name: 'refactor: 重構(既不是增加feature,也不是修復bug)'},
  {value: 'release',  name: 'release:  發佈'},
  {value: 'deploy',   name: 'deploy:   部署'},
        {value: 'test',     name: 'test:     增加測試'},
        {value: 'chore',    name: 'chore:    構建過程或輔助工具的變動(更改配置文件)'},
        {value: 'revert',   name: 'revert:   回退'},
      {value: 'build',    name: 'build:    打包'}
    ],
    // override the messages, defaults are as follows
    messages: {
        type: '請選擇提交類型:',
        customScope: '請輸入您修改的範圍(可選):',
        subject: '請簡要描述提交 message (必填):',
        body: '請輸入詳細描述(可選,待優化去除,跳過即可):',
        footer: '請輸入要關閉的issue(待優化去除,跳過即可):',
        confirmCommit: '確認使用以上信息提交?(y/n/e/h)'
    },
    allowCustomScopes: true,
    skipQuestions: ['body', 'footer'],
    subjectLimit: 72
};
  1. 交互界面測試

XkYQrJ

安裝 husky(依賴 husky v7.0.4 版本)

注意:寫文章時是依賴於 husky v7.0.4 版本,而且 husky 更新的太快了,所以使用 husky 時建議區分下版本;v8.x 以後版本使用有較大區別

如果想用最新版的 husky,建議參考官網去配置

  1. 安裝
# 1.安裝
pnpm i husky lint-staged -D
# 2.生成 .husky 的文件夾
npx husky install
# 3.添加 hooks,會在 .husky 目錄下生成一個 pre-commit 腳本文件
npx husky add .husky/pre-commit "npx --no-install lint-staged"
# 4.添加 commit-msg
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
# 5. 使用 `git commit -m "message"` 就會看到 hook 生效了。
  1. 添加配置 package.json
{
  ...
  "lint-staged": {
      "*.{js,ts}": [
            "npm run eslint",
            "npm run prettier"
      ]
  }
  ...
}

提交日誌(可選)

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