企業(yè)級前端工程化配置指南:vite4 + vue3 + ts + pinia + vue-router + axios + commit規(guī)范 + 代碼質(zhì)量檢驗(yàn)

大家好距贷,我是蘇先生,一名熱愛鉆研吻谋、樂于分享的前端工程師忠蝗,跟大家分享一句我很喜歡的話:人活著,其實(shí)就是一種心態(tài)漓拾,你若覺得快樂什湘,幸福便無處不在

你可以學(xué)到什么长赞?

  • 如何使用 vite 搭建項(xiàng)目
  • 如何集成與使用 web-localstorage-plus
  • 如何集成與使用 vue-router4
  • 如何集成與使用 pinia
  • 如何集成與使用 element-plus
  • 如何封裝axios
  • 如何借力 eslint 和 prettier 保證代碼質(zhì)量
  • 如何借力 commitlint 規(guī)范git提交信息

源碼地址

傳送門

1.創(chuàng)建項(xiàng)目

按提示選擇:

1.運(yùn)行vite

yarn create vite

2.輸入自定義的項(xiàng)目名稱

name: ? your-project-name

3.選擇你想要的技術(shù)框架

? Select a framework: ? - Use arrow-keys. Return to submit.
?   Vanilla
    Vue
    React
    Preact
    Lit
    Svelte
    Others

4.選擇ts模板

? Select a variant: ? - Use arrow-keys. Return to submit.
?   TypeScript
    JavaScript
    Customize with create-vue ↗
    Nuxt ↗

5.按提示安裝并運(yùn)行項(xiàng)目

Done. Now run:

  cd vite-project
  yarn
  yarn dev

一鍵初始化

除了上述問答形式創(chuàng)建外,vite官方也提供了快捷語法:通過命令行參數(shù)創(chuàng)建

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

# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template vue

# yarn
yarn create vite my-vue-app --template vue

# pnpm
pnpm create vite my-vue-app --template vue

2.優(yōu)化項(xiàng)目結(jié)構(gòu)

修剪vite默認(rèn)生成的項(xiàng)目結(jié)構(gòu)

1.保留public文件夾闽撤,刪除vite.svg文件得哆,同時刪除index.html中對該文件的引入

image.png

2.刪除HelloWorld.vue文件,同時從App.vue中刪除引入

image.png

3.刪除App.vue中的默認(rèn)代碼哟旗,只保留默認(rèn)的三個根元素

我個人習(xí)慣將template放到最前邊

<template>
</template>

<script setup lang="ts">
</script>

<style scoped>
</style>

4.清空assets文件夾

定制化目錄

1.創(chuàng)建store文件夾

放置關(guān)于pinia的數(shù)據(jù)狀態(tài)

2.創(chuàng)建directive文件夾

放置我們的自定義指令贩据,如:v-auth

3.創(chuàng)建utils文件夾

項(xiàng)目中的公共方法或常量

4.創(chuàng)建styles文件夾

管理公共css樣式文件,如:reset.css

5.創(chuàng)建http文件夾

處理axios的封裝和調(diào)用

6.創(chuàng)建router文件夾

管理vue-router的路由模塊

7.創(chuàng)建pages文件夾

管理業(yè)務(wù)代碼

3.配置vite.config.ts

配置別名

項(xiàng)目中闸餐,不同模塊之間往往需要互相引入饱亮,使用別名能夠幫助我們省去一級一級查找的繁瑣

...
import { resolve } from 'node:path'

export default defineConfig({
  plugins: [vue()],
  resolve:{
    alias:{
      '@':resolve(__dirname,'src')
    }
  }
})

此時,node:path__dirname會報(bào)錯舍沙,我們還需要安裝下對應(yīng)的ts類型包

yarn add @types/node --D

設(shè)置代理

我們本地開發(fā)完跟后端聯(lián)調(diào)階段近上,經(jīng)常會遇到跨域的問題,需要我們暫時的在前端進(jìn)行下處理

export default defineConfig({
  ...
  server: {
    proxy: {
      "/api": {
        target: "http url",
        changeOrigin: true,
        rewrite: (path: string) => path.replace(/^\/api/, ""),
      },
    },
  },
});

其中"/api"是我們要代理的接口標(biāo)識拂铡,target是我們實(shí)際要訪問的接口地址

設(shè)置自動導(dǎo)入

每次都手動導(dǎo)入依賴項(xiàng)是一件很麻煩的事情壹无,幸運(yùn)的是,我們可以借助第三方庫來幫我們實(shí)現(xiàn)感帅,它內(nèi)置了常見的庫斗锭,比如vue

1.安裝

yarn add unplugin-auto-import -D

2.在vite.config.ts中導(dǎo)入并作為plugin使用

...
import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
  plugins: [...,AutoImport()],
  ...
});

4.集成web-storage-plus

對于需要使用到持久緩存的地方,localstorage是優(yōu)選的方案失球,不過原生接口比較難用岖是,而該npm包對其進(jìn)行了二次封裝,使其支持了命名空間实苞、過期時間豺撑、監(jiān)聽變化、批量操作等特性黔牵,且其為我們提供了發(fā)布訂閱模式來彌補(bǔ)vue3中對bus的缺失前硫,文檔看這里:傳送門

1.安裝

yarn add web-localstorage-plus

2.在main.ts中引入并初始化根存儲

...
import createStorage from 'web-localstorage-plus'
createStorage({
    rootName:'spp-storage'
})
...

3.在.vue文件中引入并使用

<script lang="ts" setup>
import { useStorage } from 'web-localstorage-plus'

const storage = useStorage()
storage.setItem('user',{
    name:'spp',
    age:28
})
</script>

5.集成pinia

對于非持久化數(shù)據(jù),我們選擇使用pinia來進(jìn)行管理荧止,它幫我們托管了全局狀態(tài)并且提供了響應(yīng)式能力,文檔看這里:傳送門

1.安裝pinia

yarn add pinia

2.在store文件夾下新建index.ts文件作為pinia的根倉庫文件

import { createPinia } from "pinia"; 
const pinia = createPinia()
export default pinia

3.在main.ts中導(dǎo)入并將 pinia 作為 plugin 注冊給 vue

import { createApp } from 'vue'
...
import pinia from '@/store'
...
const app = createApp(App)
app.use(pinia)
...

4.在store文件夾下新建xxx.ts文件作為子存儲模塊

import { defineStore } from 'pinia'

export default defineStore('spp', {
  state() {
      return {
        spp:''
      }
  },
  actions:{
    updateSpp(spp:string){
        this.spp = spp
    }
  }
})

5.在.vue文件中使用或修改pinia的狀態(tài)

<script lang="ts" setup>
...
import useLoginStore from '@/store/login.ts'
// 獲取狀態(tài)
const store = useLoginStore()
// 修改狀態(tài)
store.updateSpp('spp')
...
</script>

6.集成vue-router4

作為spa項(xiàng)目阶剑,路由是我們進(jìn)行頁面切換的必備工具跃巡,文檔看這里:傳送門

1.使用yarn安裝

yarn add vue-router@4

2.在router文件夾下新建index.ts文件作為根路由

import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";

const routes: Array<RouteRecordRaw> = [
  {
    path: "/login",
    name: "Login",
    component: () => import("@/pages/login/index.vue")
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

3.在main.ts中導(dǎo)入并作為plugin注冊給vue

import { createApp } from 'vue'
import router from '@/router';
...
const app = createApp(App)
app.use(router)
...

4.將根App.vue作為路由出口

<template>
  <RouterView/>
</template>

7.集成less

1.安裝

yarn add less -D

2.使用

<style lang="less" scoped>
.root-app {
  .spp {
    // 自定義樣式
  }
}
</style>

8.集成element-plus

element-plus是vue側(cè)比較流行的pc端ui框架之一,文檔看這里:傳送門

1.安裝

yarn add element-plus

2.按需自動導(dǎo)入

  • 安裝依賴包
yarn add unplugin-vue-components unplugin-auto-import -D
  • 修改vite.config.ts
import { defineConfig } from "vite";
...
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";

export default defineConfig({
  plugins: [
    ...
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
  ...
});

3.在.vue文件夾下直接使用即可

9.集成axios(僅提供封裝思路牧愁,可選)

axios是目前最流行的前端發(fā)起ajax請求的庫素邪,其基于promise實(shí)現(xiàn),同時支持在瀏覽器和nodejs中使用猪半,文檔看這里:傳送門

1.安裝

yarn add axios

2.在http文件夾下新建index.ts文件

該文件對我們業(yè)務(wù)中的請求進(jìn)行基類封裝

import request from "./request";
import getPrefix from "./urlPrefix";
import { TgroupType } from '@/utils/types'
import { warn } from '@/utils/function'

class Http{
  protected prefix:string=getPrefix(undefined);
  protected config:any={}
  constructor(group?:TgroupType){
    this.prefix = getPrefix(group)
  }
  private combineUrl(url:string){
    return this.prefix + url
  }
  setExtraConfig(config:any){
    this.config = config
  }
  get<T>(url: string, arg?: T,message?:string){
    return new Promise((resolve, reject) => {
      request
        .get(this.combineUrl(url), {
          params: arg,
          ...this.config
        })
        .then((res:any)=>{
          // 根據(jù)與后端的約定format數(shù)據(jù)兔朦,并做resolve或reject
          ...
        })
        .catch(reject)
        .finally(()=>{
          this.config={}
        })
    });
  }
  post<T>(url: string, message?: string | T, arg?: T) {
    const isFull = arguments.length === 3
    if (!isFull) {
      arg = message as T;
    }
    const errMessage = '你的自定義錯誤'
    return new Promise((resolve, reject) => {
      request
        .post(this.combineUrl(url), arg,{
          ...this.config,
        })
        .then((res: any) => {
          // 根據(jù)與后端的約定format數(shù)據(jù)偷线,并做resolve或reject
          ...
        })
        .catch(()=>{
          warn(errMessage)
          reject(errMessage);
        })
        .finally(()=>{
          this.config={}
        })
    });
  }
}

export const http = new Http()
export const httpRequest = Http
export const usePrefix = getPrefix

3.在http文件夾下新建request.ts文件

該文件用于配置axios,并通過攔截器對接口狀態(tài)進(jìn)行檢測和錯誤的統(tǒng)一處理

import axios from "axios";

axios.defaults.timeout = 10000000;

axios.defaults.withCredentials = true;

axios.interceptors.request.use(
  (config) => {
    config.headers = Object.assign(config.headers,{
      // 配置header
    })
    return config;
  },
  (error) => {
    // 處理錯誤
    return Promise.reject(error);
  }
);

axios.interceptors.response.use(
  (response) => {
    // 統(tǒng)一攔截驗(yàn)證
    return response;
  },
  (error) => {
    // 處理錯誤
    return Promise.reject(error);
  }
);

export default axios;

4.在http文件夾下新建urlPrefix.ts文件

該文件用于統(tǒng)一管理不同域名對應(yīng)的不同環(huán)境下的url

import { IurlConfig,TgroupType} from '@/utils/types'
const mode = import.meta.env.MODE;
const urlConfig:IurlConfig = {
  // 前綴-{dev:'',pro:''}
}

const getPrefix = (key:TgroupType)=>{
  if(key === undefined){
    key = ''
    urlConfig[key][mode]
  }
  return urlConfig[key][mode]
}

export default getPrefix;

5.在業(yè)務(wù)中引入并使用

import { http } from "@/http";
http
    .post<傳遞與當(dāng)前接口參數(shù)匹配的類型>(url, "添加成功", params)
    .then(() => {
      // 請求成功的業(yè)務(wù)處理
    });

10.常用工具推薦

  • 時間處理:moment

文檔地址

  • 萬能工具庫:lodash-es

文檔地址

11.代碼質(zhì)量與提交規(guī)范

eslint

因?yàn)閑slint無法識別.vue文件沽甥,因此我們還需要一個定制化插件:eslint-plugin-vue声邦,文檔看這里:傳送門

1.安裝依賴

yarn add eslint eslint-plugin-vue -D

2.創(chuàng)建.eslintignore忽略文件

node_modules
dist
yarn.lock
index.html

3.添加ts支持

eslint-plugin-vue只針對.vue文件或者.js文件中的vue寫法,我們還需要對ts進(jìn)行兼容

文檔傳送門:@typescript-eslint/parser摆舟、@typescript-eslint/eslint-plugin

yarn add @typescript-eslint/parser @typescript-eslint/eslint-plugin -D

4.在根目錄下創(chuàng)建.eslintrc.cjs配置文件

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: {
      "no-console": "error",
  }
};

5.按環(huán)境區(qū)分

實(shí)際項(xiàng)目中亥曹,我們一般會根據(jù)不同的開發(fā)環(huán)境來區(qū)分規(guī)則,比如console應(yīng)在開發(fā)階段可用恨诱,生產(chǎn)時禁用

1-安裝依賴

文檔看這里:cross-env媳瞪、@rollup/plugin-eslint

yarn add cross-env @rollup/plugin-eslint -D

2-修改命令行

在打包階段配置環(huán)境變量,我這里以production為例子

"scripts": {
    ...
    "build": "cross-env NODE_ENV=production && vue-tsc --noEmit && vite build"
  },

3-將rollup插件加入vite的plugin

...
import eslint from '@rollup/plugin-eslint';

export default defineConfig({
  plugins: [
    ...
    eslint({
      include:['src/**']
    })
  ],
  ...
});

4-修改.eslintrc.cjs

cross-env會將定義的環(huán)境變量暴露在env上照宝,我們獲取并做三元判斷即可

const mode = process.env.NODE_ENV
module.exports = {
  ...
  rules: {
      "no-console": mode === 'production' ? "error" : "off",
      ...
  }
};

prettier

相關(guān)文檔看這里:prettier蛇受、eslint-config-prettiereslint-plugin-prettier

1.安裝依賴

yarn add prettier eslint-config-prettier eslint-plugin-prettier -D

2.創(chuàng)建配置文件:.prettierrc.js

以我司的某個項(xiàng)目為例

module.exports = {
  // 一行最多 150 個字符
  printWidth: 150,
  // 使用 4 個空格縮進(jìn)
  tabWidth: 4,
  // 不使用 tab 縮進(jìn)厕鹃,而使用空格
  useTabs: false,
  // 行尾需要有分號
  semi: true,
  // 使用單引號代替雙引號
  singleQuote: true,
  // 末尾使用逗號
  trailingComma: 'es5',
  // 箭頭函數(shù)兢仰,只有一個參數(shù)的時候,也需要括號
  arrowParens: 'always',
}

3.修改 .eslintrc.js 配置

強(qiáng)制當(dāng)和eslint沖突時熊响,以prettier為準(zhǔn)

module.exports = {
    ...

    extends: [
        ...
        'prettier',
        'plugin:prettier/recommended'
    ],
    ...
};

提交規(guī)范

我們需要在提交代碼前對代碼質(zhì)量旨别、代碼格式和commit信息進(jìn)行約束,為此汗茄,我們需要先注冊commit提交前鉤子

1-安裝husky

文檔看這里:傳送門

yarn add husky -D

2-初始化husky

在package.json文件夾下新增prepare腳本秸弛,并立即運(yùn)行一次

"scripts": {
    ...
    "prepare": "husky install"
  },

3-注冊hook

我們使用pre-commit鉤子來攔截提交行為,

npx husky add .husky/pre-commit "npm run check"
git add .husky/pre-commit

此時洪碳,當(dāng)git commit發(fā)生時递览,將會調(diào)用check腳本,但這默認(rèn)事針對全部文件的瞳腌,因此我們需要借助另一個npm包幫我們把當(dāng)前更改的文件提取出來單獨(dú)校驗(yàn)

1-安裝lint-staged

文檔看這里:傳送門

yarn add lint-staged -D

2-修改package.json配置

"lint-staged": {
    "*.{js,ts,vue}": [
      "npm run eslint",
      "prettier --parser=typescript --write"
    ]
}

3-與husky關(guān)聯(lián)

將lint-staged作為check的指向腳本

"scripts": {
    ...
    "check": "lint-staged"
  },

最后我們來對commit提交格式進(jìn)行約束绞铃,這可以通過commitlint來幫我們完成,文檔看這里:傳送門

1-安裝

yarn add @commitlint/config-conventional @commitlint/cli -D

2-將其校驗(yàn)位置放在check腳本執(zhí)行前

npx husky add .husky/commit-msg  'npx --no -- commitlint --edit ${1}'

3-創(chuàng)建配置文件commitlint.config.cjs

module.exports = {
    extends: ['@commitlint/config-conventional'],
    rules: {
        'type-enum': [
            2,
            'always',
            [
                'feature', // 迭代功能
                'conf', // 修改構(gòu)建配置
                'fixbug', // 修復(fù)bug
                'refactor', // 代碼重構(gòu)
                'optimize', // 代碼優(yōu)化
                'style', // 僅修改樣式文件
                'docs', // 文檔補(bǔ)充說明
            ],
        ],
        'header-max-length': [0, 'always', 72], //限制最長72
    },
};

4-測試使用

image.png

如果本文對您有用嫂侍,希望能得到您的點(diǎn)贊和收藏


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末儿捧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子挑宠,更是在濱河造成了極大的恐慌菲盾,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件各淀,死亡現(xiàn)場離奇詭異懒鉴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門临谱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來璃俗,“玉大人,你說我怎么就攤上這事悉默〕腔恚” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵麦牺,是天一觀的道長钮蛛。 經(jīng)常有香客問我,道長剖膳,這世上最難降的妖魔是什么魏颓? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮吱晒,結(jié)果婚禮上甸饱,老公的妹妹穿的比我還像新娘。我一直安慰自己仑濒,他們只是感情好叹话,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著墩瞳,像睡著了一般驼壶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喉酌,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天热凹,我揣著相機(jī)與錄音,去河邊找鬼泪电。 笑死般妙,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的相速。 我是一名探鬼主播碟渺,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼突诬!你這毒婦竟也來了苫拍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤旺隙,失蹤者是張志新(化名)和其女友劉穎绒极,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體催束,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年伏社,在試婚紗的時候發(fā)現(xiàn)自己被綠了抠刺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塔淤。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖速妖,靈堂內(nèi)的尸體忽然破棺而出高蜂,到底是詐尸還是另有隱情,我是刑警寧澤罕容,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布备恤,位于F島的核電站,受9級特大地震影響锦秒,放射性物質(zhì)發(fā)生泄漏露泊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一旅择、第九天 我趴在偏房一處隱蔽的房頂上張望惭笑。 院中可真熱鬧,春花似錦生真、人聲如沸沉噩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽川蒙。三九已至,卻和暖如春长已,著一層夾襖步出監(jiān)牢的瞬間畜眨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工痰哨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胶果,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓斤斧,卻偏偏與公主長得像早抠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子撬讽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容