逐步搭建出團(tuán)隊(duì)的 vue3 前端架構(gòu)

作者:codexu
https://juejin.cn/post/7025524870842679310
前言
由于?vue3.2?版本的發(fā)布,
復(fù)制代碼
4.4 封裝?SVG?的圖標(biāo)組件
svg 圖標(biāo)比較小,而且都是可讀的 xml 文本,所以我們把它直接放在項(xiàng)目中即可,通過(guò)?vite-plugin-svg-icons?插件,實(shí)現(xiàn)自動(dòng)引入 svg 圖標(biāo)。
配置 vite.config.ts:
plugins:?[
??viteSvgIcons({
????iconDirs:?[resolve(process.cwd(),?'src/assets/icons')],
????symbolId:?'icon-[dir]-[name]',
??}),
]
復(fù)制代碼
封裝一個(gè) vue 組件:
復(fù)制代碼
首先將下載的 .svg 圖標(biāo)放入 @/assets/icons 文件夾下
復(fù)制代碼
name 放置在 @/assets/icons 文件夾下的文件名。 color 顏色填充,使用此項(xiàng)會(huì)默認(rèn)覆蓋圖標(biāo)顏色。
5.按需自動(dòng)引入組件
unplugin-vue-components[77]?是一款非常強(qiáng)大的插件(極力推薦),核心功能就是幫助你自動(dòng)按需引入組件,Tree-shakable,只注冊(cè)你使用的組件。這里說(shuō)一下他的兩個(gè)核心使用方式和配置方式。
此插件不僅支持 vue3,同時(shí)也支持 vue2,并且支持 Vite、Webpack、Vue CLI、Rollup。
5.1 安裝與配置
安裝:
npm?i?unplugin-vue-components?-D
復(fù)制代碼
配置:
//?vite.config.ts
import?Components?from?'unplugin-vue-components/vite'
export?default?defineConfig({
??plugins:?[
????Components({?/*?options?*/?}),
??],
})
復(fù)制代碼
這里的 options 可以配置一些選項(xiàng),后面提到的組件庫(kù)注冊(cè)會(huì)使用到。
5.2 改變?nèi)纸M件注冊(cè)方式
我們通常將全局的組件封裝在?@/src/components?中,然后通過(guò)?app.component()?注冊(cè)全局組件。使用此插件后,無(wú)需手寫(xiě)注冊(cè),直接在模板中使用組件即可:
這里引入官方的示例:
復(fù)制代碼
自動(dòng)編譯為:
復(fù)制代碼
5.3 自動(dòng)引入組件庫(kù)
在使用組件庫(kù)時(shí),常規(guī)組件我們也會(huì)注冊(cè)到全局,如果使用局部注冊(cè)由于頁(yè)面中會(huì)使用到多個(gè)組件,會(huì)非常麻煩,所以這個(gè)功能絕佳,例如我們使用 ant-design-vue 組件庫(kù)。
直接在模板中使用即可,無(wú)需手動(dòng)注冊(cè)或局部引用:
按鈕
復(fù)制代碼
當(dāng)然,你還需要在 vite 中引入它的解析器:
import?Components?from?'unplugin-vue-components/vite'
import?{?AntDesignVueResolver?}?from?'unplugin-vue-components/resolvers'
export?default?defineConfig({
??plugins:?[
????Components({
??????resolvers:?[
????????AntDesignVueResolver(),
??????]
????})
??],
})
復(fù)制代碼
目前支持的解析器,根據(jù)你的喜好去選擇:
Ant Design Vue[78] Element Plus[79] Element UI[80] Headless UI[81] IDux[82] Naive UI[83] Prime Vue[84] Vant[85] VEUI[86] Varlet UI[87] View UI[88] Vuetify[89] VueUse Components[90] Quasar[91]
6.樣式
項(xiàng)目中最好使用通用樣式,可以創(chuàng)建?src/styles?目錄存放,這里推薦一些分類:
styles
??├──?antd?#?組件庫(kù)樣式覆蓋,命名自取,這里以?ant?design?為例
??├──?color.less?#?顏色
??├──?index.less?#?入口
??├──?global.less?#?公共類
??├──?transition.less?#?動(dòng)畫(huà)相關(guān)
??└──?variable.less?#?變量
復(fù)制代碼
6.1 預(yù)設(shè)基礎(chǔ)樣式
相信用過(guò)?normalize[92]?的同學(xué)不在少數(shù),它可以重置 css 樣式,使各瀏覽器效果保持一致。后面的章節(jié)會(huì)提到 tailwind.css,它內(nèi)置了預(yù)設(shè)樣式重置的功能,與 normalize 還是有一定的區(qū)別,有興趣的同學(xué)可以了解一下[93]。
6.2 CSS 預(yù)處理器
雖然 vite 原生支持 less/sass/scss/stylus,但是你必須手動(dòng)安裝他們的預(yù)處理器依賴,例如:
npm?install?-D?less
復(fù)制代碼
如何選擇預(yù)處理器?
推薦使用你是所使用的組件庫(kù)的樣式語(yǔ)言,因?yàn)?css 預(yù)處理器學(xué)會(huì)一種后,入手其他幾乎沒(méi)有學(xué)習(xí)成本。
6.3 開(kāi)啟 scoped
沒(méi)有加 scoped 屬性,會(huì)編譯成全局樣式,造成全局污染。
復(fù)制代碼
6.4 深度選擇器
有時(shí)我們可能想明確地制定一個(gè)針對(duì)子組件的規(guī)則。
如果你希望 scoped 樣式中的一個(gè)選擇器能夠作用得“更深”,例如影響子組件,你可以使用 >>> 操作符。有些像 Sass 之類的預(yù)處理器無(wú)法正確解析 >>>。這種情況下你可以使用 /deep/ 或 ::v-deep 操作符取而代之——兩者都是 >>> 的別名,同樣可以正常工作。
7.布局
頁(yè)面整體布局是一個(gè)產(chǎn)品最外層的框架結(jié)構(gòu),往往會(huì)包含導(dǎo)航、頁(yè)腳、側(cè)邊欄等。在頁(yè)面之中,也有很多區(qū)塊的布局結(jié)構(gòu)。在真實(shí)項(xiàng)目中,頁(yè)面布局通常統(tǒng)領(lǐng)整個(gè)應(yīng)用的界面,有非常重要的作用,所以單獨(dú)拆分出來(lái)也是非常有必要的。
在腳手架中,所有的通用布局組件都應(yīng)該放在 src/layouts 中,這種封裝比較簡(jiǎn)單,這里就不貼代碼了,大家按照自己實(shí)際情況自行發(fā)揮,在此僅提供一下封裝思路。
7.1 常規(guī)的布局
BasicLayout
基礎(chǔ)頁(yè)面布局,包含了頭部導(dǎo)航,側(cè)邊欄等。
BlankLayout
空白的布局。
7.2 特殊的布局
RouteLayout
如果你的項(xiàng)目在路由切換中需要對(duì)某些二級(jí)頁(yè)面進(jìn)行緩存,那么推薦你創(chuàng)建一個(gè) RouteLayout,通過(guò)路由?meta?中的配置,返回?router-view?或者使用?keep-alive?包裹的?router-view。
UserLayout
用于用戶登錄注冊(cè)等頁(yè)面抽離出來(lái)。
PageLayout
基礎(chǔ)布局,包含了面包屑等信息,內(nèi)含 slot。
8.集成 Tailwind.css
Tailwind.css[94]?在我第一次看到它的時(shí)候,內(nèi)心是比較反感的,但實(shí)際上手之后又覺(jué)得真香。從 vue2 項(xiàng)目中,我已經(jīng)引入了 tailwind,整體的開(kāi)發(fā)結(jié)果就是,基本很少再使用??標(biāo)簽去轉(zhuǎn)本定義一些 class 和樣式,畢竟起名字這種事,一個(gè)是涉及到規(guī)范,一個(gè)是涉及到英語(yǔ)。如果你選擇 tailwind,CSS 預(yù)處理器的作用就會(huì)顯得微乎其微,因?yàn)槟銦o(wú)需再自定定義各種變量和 mixins。
總體來(lái)說(shuō),學(xué)習(xí)成本并不高,花上兩個(gè)小時(shí)足夠上手,記住不用死記硬背那些類名。
8.1 效率提升
很多人總是說(shuō)樣式要與 HTML 分離,現(xiàn)在為什么又要提倡 tailwind 這種與 HTML 緊密結(jié)合的工具?這是因?yàn)楝F(xiàn)在使用 vue 這類框架已經(jīng)高度組件化,樣式分離是為了方便復(fù)用和維護(hù),但在組件化面前樣式分離只能是降低開(kāi)發(fā)效率。
下面介紹一下 tailwind 提供了哪些提升效率的功能:
提供了大量的功能類,極大的提高了可維護(hù)性。 響應(yīng)式設(shè)計(jì),各種設(shè)備一把梭。 懸停、焦點(diǎn)和其它狀態(tài)。 深色模式。 支持配置,例如顏色方面很難做到跟你的設(shè)計(jì)師統(tǒng)一。 不用為起名字而糾結(jié)???
8.2 JIT 模式
如果你的環(huán)境支持 postcss8( vue/cli 構(gòu)建的 vue2 項(xiàng)目是 postcss7 ),那么?JIT?模式直接帶你起飛。
超快的構(gòu)建速度。 支持變體,你甚至可以這么寫(xiě)? sm:hover:active:disabled:opacity-75。支持任意樣式,例如? md:top-[-113px]。開(kāi)發(fā)和生產(chǎn)環(huán)境結(jié)果是一致的,(我在 vue2 項(xiàng)目中就遇到過(guò)組件庫(kù)構(gòu)建結(jié)果不一致的問(wèn)題)。
如果你使用 vscode 那你一定要安裝?Tailwind CSS IntelliSense[95]?插件,它可以自動(dòng)補(bǔ)全類名,顯著降低學(xué)習(xí)成本。
8.3 關(guān)于打包體積
使用默認(rèn)配置,未壓縮是?3739.4kB?,Gzip壓縮 是 293.9kB,Brotli壓縮 是 73.2kB。這似乎看起來(lái)很大,這是因?yàn)?tailwind 提供了成千上萬(wàn)的功能類,其中絕大部分你不會(huì)使用到。
當(dāng)構(gòu)建生產(chǎn)時(shí),你應(yīng)該使用 purge 選項(xiàng)來(lái)?tree-shake?優(yōu)化未使用的樣式,并優(yōu)化您的最終構(gòu)建大小當(dāng)使用 Tailwind 刪除未使用的樣式時(shí),很難最終得到超過(guò)?10kb?的壓縮 CSS。
還有一點(diǎn),Atom CSS?極大的提升了樣式的復(fù)用程度,從而直接降低了構(gòu)建體積。
9.vuex 替代方案 pinia
由于?vuex 4?對(duì) typescript 的支持讓人感到難過(guò),所以狀態(tài)管理?xiàng)売昧?vuex 而采取了?pinia[96]。
忘記在哪看到,尤大好像說(shuō)?pinia[97]?可能會(huì)代替 vuex,所以請(qǐng)放心使用。
9.1 為什么采用 Pinia ?
Pinia?的 API 設(shè)計(jì)非常接近? Vuex 5?的提案[98]。(作者是 Vue 核心團(tuán)隊(duì)成員)無(wú)需像? Vuex 4?自定義復(fù)雜的類型來(lái)支持 typescript,天生具備完美的類型推斷。模塊化設(shè)計(jì),你引入的每一個(gè) store 在打包時(shí)都可以自動(dòng)拆分他們。 無(wú)嵌套結(jié)構(gòu),但你可以在任意的 store 之間交叉組合使用。 Pinia?與?Vue devtools?掛鉤,不會(huì)影響 Vue 3 開(kāi)發(fā)體驗(yàn)。
下面簡(jiǎn)單的介紹一下如何使用 Pinia,并對(duì)比 vuex 有哪些區(qū)別與注意事項(xiàng),具體請(qǐng)參考官方文檔[99]。
9.2 創(chuàng)建 Store
Pinia 已經(jīng)內(nèi)置在腳手架中,并且與 vue 已經(jīng)做好了關(guān)聯(lián),你可以在任何位置創(chuàng)建一個(gè) store:
import?{?defineStore?}?from?'pinia'
export?const?useUserStore?=?defineStore({
??id:?'user',
??state:?()?=>({}),
??getters:?{},
??actions:?{}
})
復(fù)制代碼
這與 Vuex 有很大不同,它是標(biāo)準(zhǔn)的 Javascript 模塊導(dǎo)出,這種方式也讓開(kāi)發(fā)人員和你的 IDE 更加清楚 store 來(lái)自哪里。
Pinia 與 Vuex 的區(qū)別:
id?是必要的,它將所使用 store 連接到 devtools。 創(chuàng)建方式: new Vuex.Store(...)(vuex3),createStore(...)(vuex4)。對(duì)比于 vuex3 ,state 現(xiàn)在是一個(gè)函數(shù)返回對(duì)象。 沒(méi)有?mutations,不用擔(dān)心,state 的變化依然記錄在 devtools 中。
9.3 State
創(chuàng)建好 store 之后,可以在 state 中創(chuàng)建一些屬性了:
state:?()?=>?({?name:?'codexu',?age:?18?})
復(fù)制代碼
將 store 中的 state 屬性設(shè)置為一個(gè)函數(shù),該函數(shù)返回一個(gè)包含不同狀態(tài)值的對(duì)象,這與我們?cè)诮M件中定義數(shù)據(jù)的方式非常相似。
在模板中使用 store:
現(xiàn)在我們想從 store 中獲取到 name 的狀態(tài),我們只需要使用以下的方式即可:
{{userStore.name}}</h1>
const?userStore?=?useUserStore()
return?{?userStore?}
復(fù)制代碼
注意這里并不需要?userStore.state.name。
雖然上面的寫(xiě)法很舒適,但是你一定不要用解構(gòu)的方式去提取它內(nèi)部的值,這樣做的話,會(huì)失去它的響應(yīng)式:
const?{?name,?email?}?=?useUserStore()
復(fù)制代碼
9.4 Getters
Pinia 中的 getter 與 Vuex 中的 getter 、組件中的計(jì)算屬性具有相同的功能,傳統(tǒng)的函數(shù)聲明使用 this 代替了 state 的傳參方法,但箭頭函數(shù)還是要使用函數(shù)的第一個(gè)參數(shù)來(lái)獲取 state ,因?yàn)榧^函數(shù)處理 this 的作用范圍:
getters:?{
??nameLength()?{
????return?this.name.length
??},
??nameLength:?state?=>?state.name.length,
??nameLength:?()=>?this.name.length???
}
復(fù)制代碼
9.5 Actions
這里與 Vuex 有極大的不同,Pinia 僅提供了一種方法來(lái)定義如何更改狀態(tài)的規(guī)則,放棄 mutations 只依靠 Actions,這是一項(xiàng)重大的改變。
Pinia 讓 Actions 更加的靈活:
可以通過(guò)組件或其他?action?調(diào)用 可以從其他 store?的 action 中調(diào)用 直接在商店實(shí)例上調(diào)用 支持同步或異步 有任意數(shù)量的參數(shù) 可以包含有關(guān)如何更改狀態(tài)的邏輯(也就是 vuex 的 mutations 的作用) 可以? $patch?方法直接更改狀態(tài)屬性
actions:?{
??async?insertPost(data){
????await?doAjaxRequest(data);
????this.name?=?'...';
??}
}
復(fù)制代碼
9.6 Devtools
腳手架已內(nèi)置下面的代碼,這將添加 devtools 支持:
import?{?createPinia,?PiniaPlugin?}?from?'pinia'
Vue.use(PiniaPlugin)
const?pinia?=?createPinia()
復(fù)制代碼
時(shí)間旅行功能貌似已經(jīng)可以使用了,這塊后續(xù)會(huì)關(guān)注。
10.基于 mitt 處理組件間事件聯(lián)動(dòng)
如果你曾經(jīng)是 Vue2.x 的開(kāi)發(fā)者,那么請(qǐng)閱讀下面引用官方文檔[100]的一段話:
我們從實(shí)例中完全移除了?
$on、$off?和?$once?方法。$emit?仍然包含于現(xiàn)有的 API 中,因?yàn)樗糜谟|發(fā)由父組件聲明式添加的事件處理函數(shù)。在 Vue 3 中,已經(jīng)不可能使用這些 API 從組件內(nèi)部監(jiān)聽(tīng)組件自己發(fā)出的事件了,該用例暫沒(méi)有遷移的方法。但是該 eventHub 模式可以被替換為實(shí)現(xiàn)了事件觸發(fā)器接口的外部庫(kù),例如?
mitt?或?tiny-emitter。
10.1 為什么選擇 mitt ?
足夠小,僅有 200bytes。 支持全部事件的監(jiān)聽(tīng)和批量移除。 無(wú)依賴,不論是什么框架都可以直接使用。
10.2 嚴(yán)重警告
我們已經(jīng)無(wú)法在項(xiàng)目中使用?eventBus,僅推薦你在特殊場(chǎng)合下使用 mitt,它并不是開(kāi)發(fā)的常態(tài),你一定要確保知道自己在做什么?否則你的項(xiàng)目將難以維護(hù)!!!
10.3 如何使用 mitt ?
在使用 mitt 前建議請(qǐng)閱讀官方文檔[101]:
腳手架默認(rèn)提供一個(gè)可以直接使用的對(duì)象:
import?emitter?from?'@/libs/emitter';
復(fù)制代碼
當(dāng)然你也可以引入已經(jīng)安裝好的 mitt:
import?mitt?from?'mitt'
const?emitter?=?mitt()
復(fù)制代碼
mitt 提供了非常簡(jiǎn)單的 API,下面代碼是官方演示:
//?listen?to?an?event
emitter.on('foo',?e?=>?console.log('foo',?e)?)
//?listen?to?all?events
emitter.on('*',?(type,?e)?=>?console.log(type,?e)?)
//?fire?an?event
emitter.emit('foo',?{?a:?'b'?})
//?clearing?all?events
emitter.all.clear()
//?working?with?handler?references:
function?onFoo()?{}
emitter.on('foo',?onFoo)???//?listen
emitter.off('foo',?onFoo)??//?unlisten
復(fù)制代碼
11.異步請(qǐng)求
絕大多數(shù)項(xiàng)目想必逃脫不了接口的對(duì)接,如果你的項(xiàng)目存在大量的接口,我建議做到以下幾點(diǎn):
封裝請(qǐng)求。 統(tǒng)一的 API 接口管理。 Mock 數(shù)據(jù)功能(根據(jù)需求斟酌使用)。
上述的主要目的就是在幫助我們簡(jiǎn)化代碼和利于后期的更新維護(hù)。
11.1 基于 axios 的封裝
相信開(kāi)發(fā)過(guò) vue2 項(xiàng)目的同學(xué)已經(jīng)對(duì) axios 非常熟悉的,在這里提供一些封裝的思路:
通過(guò)? import.meta.env.VITE_APP_BASE_URL?獲取環(huán)境變量,配置?baseURL,如果接口存在多個(gè)不同域名,可以通過(guò) js 變量控制。設(shè)置? timeout?請(qǐng)求超時(shí)、斷網(wǎng)情況處理。設(shè)置請(qǐng)求頭,攜帶? token。異常攔截處理,后端通過(guò)你攜帶的? token?判斷你是否過(guò)期,如果返回?401?你可能需要跳轉(zhuǎn)到登錄頁(yè)面,并提示需要重新登錄。響應(yīng)攔截,通常后端返回 code、data、msg,如果是請(qǐng)求正常,我們可以直接返回 data 數(shù)據(jù),如果是異常的 code,我們也可以在這里直接彈出報(bào)錯(cuò)提示。 無(wú)感刷新 token,如果你的 token 過(guò)期,可以通過(guò)后端返回的 refreshToken 調(diào)用刷新接口,獲取新的 token。當(dāng)然這里涉及到很多細(xì)節(jié),例如終端請(qǐng)求、重新發(fā)送請(qǐng)求、重新請(qǐng)求列隊(duì)。 中斷請(qǐng)求,例如頁(yè)面切換時(shí),我們要中斷正在發(fā)生的請(qǐng)求。
相關(guān)代碼(僅供參考)[102]
11.2 為 axios 增加泛型的支持
到目前為止,axios 請(qǐng)求返回的類型是 any,這時(shí)我們對(duì)請(qǐng)求后的數(shù)據(jù)進(jìn)行操作時(shí),沒(méi)有享受到 ts 帶來(lái)的類型提示,這顯然不符合我們的預(yù)期。
這時(shí)我們要做的就是重新聲明 axios 模塊:新建一個(gè) shims.d.ts,然后在調(diào)用時(shí)加上泛型。
import?{?AxiosRequestConfig?}?from?'axios';
declare?module?'axios'?{
??export?interface?AxiosInstance?{
????any>(config:?AxiosRequestConfig):?Promise;
????requestany>(config:?AxiosRequestConfig):?Promise;
????getany>(url:?string,?config?:?AxiosRequestConfig):?Promise;
????deleteany>(url:?string,?config?:?AxiosRequestConfig):?Promise;
????headany>(url:?string,?config?:?AxiosRequestConfig):?Promise;
????postany>(url:?string,?data?:?any,?config?:?AxiosRequestConfig):?Promise;
????putany>(url:?string,?data?:?any,?config?:?AxiosRequestConfig):?Promise;
????patchany>(url:?string,?data?:?any,?config?:?AxiosRequestConfig):?Promise;
??}
}
復(fù)制代碼
做好這一步后,你就必須在創(chuàng)建接口時(shí),聲明請(qǐng)求相應(yīng)數(shù)據(jù)的類型。
11.3 封裝更方便的 useRequest
設(shè)想一下,編寫(xiě)請(qǐng)求代碼時(shí),我們通常會(huì)定義這么幾個(gè)變量:
data: 儲(chǔ)存請(qǐng)求數(shù)據(jù) loading: 請(qǐng)求加載狀態(tài)
尤其是 loading,我們需要在請(qǐng)求前設(shè)置為 true,請(qǐng)求結(jié)束后設(shè)置為 false。
上面的封裝方式,是對(duì)基礎(chǔ)的功能封裝,因?yàn)槲覀冊(cè)谑褂?vue3,所以可以進(jìn)行再一次的封裝成為?hook,我們使用起來(lái)會(huì)更加方便。
例如下面這個(gè)樣子:
使用 useRequest 定義一個(gè)接口:
export?default?getUserInfo(id)?{
??return?useRequest({
????method:?'get',
????url:?'/api/user',
????params:?{?id?}
??})
}
復(fù)制代碼
使用此接口:
const?{?data,?loading?}?=?getUserInfo();
復(fù)制代碼
注意這里的 data 是響應(yīng)式的。
這是我想到的一種思路,目前還沒(méi)有做很好的封裝,相關(guān)代碼僅供參考[103],你也可以借鑒一些成熟方案,比如 vueuse 中的?useFetch[104],但是他是基于 Fetch API 設(shè)計(jì)的,并不符合我的預(yù)期要求,有更好的方案請(qǐng)大家在下面留言。
11.4 統(tǒng)一的 API 接口管理
自從前端和后端分家之后,前后端接口對(duì)接就成為了常態(tài),而對(duì)接接口的過(guò)程就離不開(kāi)接口文檔,比較主流就是 Swagger,但是如何在前端項(xiàng)目中更好的去管理跟后端對(duì)接的接口呢?
在 src 目錄中 創(chuàng)建 api 目錄,內(nèi)部目錄應(yīng)按照后端制定的模塊創(chuàng)建。
每個(gè)模塊中創(chuàng)建多個(gè) ts 文件,一個(gè)接口應(yīng)對(duì)應(yīng)一個(gè) ts 文件,其中包含了以下內(nèi)容:
請(qǐng)求參數(shù)的類型聲明。 響應(yīng)數(shù)據(jù)的類型聲明。 返回定義好的請(qǐng)求函數(shù)(url、method、params、data 等)。
統(tǒng)一去定義和管理 API 接口,只要后端規(guī)范的命名和你認(rèn)真的寫(xiě)好類型聲明,對(duì)前端來(lái)說(shuō) typescript 就是最好的接口文檔。
11.5 mock
vite 使用 mock 數(shù)據(jù)非常簡(jiǎn)單,你可以使用?vite-plugin-mock[105]?插件,如果你了解 mockjs,你可以快速上手。
12.路由
路由和菜單是組織起一個(gè)應(yīng)用的關(guān)鍵骨架。
12.1 創(chuàng)建路由三部曲
通常一個(gè)項(xiàng)目需要做到這幾步:
使用?createRouter?創(chuàng)建路由,這時(shí)候根據(jù)需求選擇?Hash?路由或者?History?路由。 根據(jù)業(yè)務(wù)需求配置路由,注意這里很可能就用到前文提到過(guò)的布局組件。 如果有權(quán)限相關(guān)的業(yè)務(wù),你需要?jiǎng)?chuàng)建?permission.ts?在路由鉤子觸發(fā)時(shí)做一些事情。
如果你的頁(yè)面比較多,建議你創(chuàng)建 routes 目錄,分模塊聲明路由。
參考代碼[106]
12.2 使用 meta 豐富你的路由
vue-router4.x 支持 typescript,配置路由的類型是 RouteRecordRaw,這里 meta 可以讓我們有更多的發(fā)揮空間,這里提供一些參考:
title: string; 頁(yè)面標(biāo)題,通常必選。 icon?: string; 圖標(biāo),一般配合菜單使用。 auth?: boolean; 是否需要登錄權(quán)限。 ignoreAuth?: boolean; 是否忽略權(quán)限。 roles?: RoleEnum[]; 可以訪問(wèn)的角色 keepAlive?: boolean; 是否開(kāi)啟頁(yè)面緩存 hideMenu?: boolean; 有些路由我們并不想在菜單中顯示,比如某些編輯頁(yè)面。 order?: number; 菜單排序。 frameUrl?: string; 嵌套外鏈。
這里只提供一些思路,每個(gè)項(xiàng)目多多少少會(huì)涉及到這些問(wèn)題,具體如何實(shí)現(xiàn)請(qǐng)查閱資料自行解決。
13.項(xiàng)目性能與細(xì)節(jié)優(yōu)化
13.1 開(kāi)啟 gzip
開(kāi)啟 gzip 可以極大的壓縮靜態(tài)資源,對(duì)頁(yè)面加載的速度起到了顯著的作用。
使用?vite-plugin-compression[107]?可以?gzip?或?brotli?的方式來(lái)壓縮資源,這一步需要服務(wù)器端的配合,vite 只能幫你打包出?.gz?文件。此插件使用簡(jiǎn)單,你甚至無(wú)需配置參數(shù),引入即可。
13.2 頁(yè)面載入進(jìn)度條
頁(yè)面路由切換時(shí),附帶一個(gè)加載進(jìn)度條會(huì)顯得非常友好,不至于白屏?xí)r間過(guò)長(zhǎng),讓用戶以為頁(yè)面假死。
這時(shí)候我們可以用到?nprogress[108],在路由切換時(shí)開(kāi)啟和關(guān)閉:
import?NProgress?from?'nprogress';
router.beforeEach(async?(to,?from,?next)?=>?{
??NProgress.start();
});
router.afterEach((to)?=>?{
??NProgress.done();
});
復(fù)制代碼
13.3 Title
在不同的路由下顯示不同的標(biāo)題是常規(guī)的操作,我們可以通過(guò)路由鉤子獲取 meta 中的 title 屬性改變標(biāo)簽頁(yè)上的 title。
你可以使用 vueuse 提供的?useTitle[109],或者 window.document.title 自行封裝。
你也可以通過(guò)環(huán)境變量將你的主標(biāo)題拼接在路由標(biāo)題的后面:
const?{?VITE_APP_TITLE?}?=?import.meta.env;
復(fù)制代碼
13.4 解決移動(dòng)端使用 vh 的問(wèn)題
有興趣的同學(xué)可以嘗試一下 chrome 移動(dòng)端瀏覽器上的 100vh,是真正的視口高度的 100% 嘛。
為了解決這一問(wèn)題,我們可以通過(guò) postCss 插件解決。
安裝?postcss-viewport-height-correction[110]?插件:
npm?install?-D?postcss-viewport-height-correction
復(fù)制代碼
在 postcss.config.js 中增加 plugin:
module.exports?=?{
??plugins:?{
????'postcss-viewport-height-correction':?{},
??},
}
復(fù)制代碼
添加這一段 js 代碼在全局,你可以直接添加在 index.html 上即可:
const?customViewportCorrectionVariable?=?'vh';
function?setViewportProperty(doc)?{
??let?prevClientHeight;
??const?customVar?=?`--${customViewportCorrectionVariable?||?'vh'}`;
??function?handleResize()?{
????const?{?clientHeight?}?=?doc;
????if?(clientHeight?===?prevClientHeight)?return;
????requestAnimationFrame(function?updateViewportHeight()?{
??????doc.style.setProperty(customVar,?`${clientHeight?*?0.01}px`);
??????prevClientHeight?=?clientHeight;
????});
??}
??handleResize();
??return?handleResize;
}
window.addEventListener('resize',?setViewportProperty(document.documentElement));
復(fù)制代碼
13.5 可以常駐的 JavaScript 庫(kù)
前文提到過(guò)的?vueuse[111],非常強(qiáng)大,強(qiáng)烈建議嘗試。 lodash[112],用了都說(shuō)好,早用早下班。
14.代碼風(fēng)格與流程規(guī)范
14.1 ESLint
不管是多人合作還是個(gè)人項(xiàng)目,代碼規(guī)范都是很重要的。這樣做不僅可以很大程度地避免基本語(yǔ)法錯(cuò)誤,也保證了代碼的可讀性。
這里推薦使用?airbnb?規(guī)范。
配置參考[113]
14.2 StyleLint
盡管前文提到過(guò) tailwind,可以讓你幾乎不寫(xiě) css,但是涉及到團(tuán)隊(duì)協(xié)作,這一點(diǎn)也要嚴(yán)謹(jǐn)。
StyleLint 是一個(gè)強(qiáng)大的、現(xiàn)代化的 CSS 檢測(cè)工具, 與 ESLint 類似, 是通過(guò)定義一系列的編碼風(fēng)格規(guī)則幫助我們避免在樣式表中出現(xiàn)錯(cuò)誤,配合編輯器的自動(dòng)修復(fù),可以很好的統(tǒng)一團(tuán)隊(duì)項(xiàng)目 css 風(fēng)格。
配置參考[114]
14.3 代碼提交規(guī)范
在多人協(xié)作的背景下,git 倉(cāng)庫(kù)和 workflow 的作用很重要。而對(duì)于 commit 提交的信息說(shuō)明存在一定規(guī)范,現(xiàn)使用?commitlint?+?husky?規(guī)范 git commit -m "" 中的描述信息。我們都知道,在使用 git commit 時(shí),git 會(huì)提示我們填入此次提交的信息。可不要小看了這些 commit,團(tuán)隊(duì)中規(guī)范了 commit 可以更清晰的查看每一次代碼提交記錄,還可以根據(jù)自定義的規(guī)則,自動(dòng)生成 changeLog 文件。
提交格式(注意冒號(hào)后面有空格):
<type>[optional?scope]:?
復(fù)制代碼
type :用于表明我們這次提交的改動(dòng)類型。 optional scope:可選,用于標(biāo)識(shí)此次提交主要涉及到代碼中哪個(gè)模塊。 description:一句話描述此次提交的主要內(nèi)容,做到言簡(jiǎn)意賅。
Type 類型
build:編譯相關(guān)的修改,例如發(fā)布版本、對(duì)項(xiàng)目構(gòu)建或者依賴的改動(dòng) chore:其他修改, 比如改變構(gòu)建流程、或者增加依賴庫(kù)、工具等 ci:持續(xù)集成修改 docs:文檔修改 feat:新特性、新功能 fix:修改bug perf:優(yōu)化相關(guān),比如提升性能、體驗(yàn) refactor:代碼重構(gòu) revert:回滾到上一個(gè)版本 style:代碼格式修改, 注意不是 css 修改 test:測(cè)試用例修改
關(guān)于 commitlint + husky 的配置文章有很多,大同小異,請(qǐng)根據(jù)自己的實(shí)際情況配置。
15.編寫(xiě)使用文檔
做到這一步,你的整個(gè)腳手架開(kāi)發(fā)已經(jīng)接近于尾聲,但是你做了這么多,你的同事并不知道如何使用,甚至你過(guò)一段時(shí)間也會(huì)忘記,所以你必須養(yǎng)成良好的編寫(xiě)文檔習(xí)慣。
15.1 使用 vitepress 搭建文檔
這里我推薦使用 vuepress 或者 vitepress,說(shuō)實(shí)話你只寫(xiě)文檔 vitepress 會(huì)讓你更舒服,因?yàn)樗芸臁?/p>
vitepress[115]?很適合構(gòu)建博客網(wǎng)站、技術(shù)文檔,就是因?yàn)樗梢灾苯佑?markdown 進(jìn)行書(shū)寫(xiě),所有寫(xiě)過(guò)博客的人,都應(yīng)該對(duì)它不陌生。一個(gè) .md 文件,即可生成一張頁(yè)面,十分方便。
創(chuàng)建一個(gè) vitepress 文檔實(shí)在是太過(guò)于簡(jiǎn)單,你可以參考官方文檔,或者參考我的文檔[116]。
15.2 文檔部署
如果你的團(tuán)隊(duì)可以幫助你搭建 CI/CD 自動(dòng)部署是再好不過(guò)了,如果沒(méi)有這個(gè)條件,你也可以通過(guò) github 提供的 actions 功能,完成自動(dòng)部署。
代碼參考[117]
16.插件
如果你想更痛快的用上述功能,建議你安裝下面的插件。
16.1 VSCode 插件
Vue Language Features \(Volar\)[118],你現(xiàn)在查 Volar 可能找不到,你需要的是這個(gè)。 Vue 3 Snippets[119],vue3 快捷輸入。 Tailwind CSS IntelliSense[120],tailwind 代碼提示。 Stylelint[121] Prettier - Code formatter[122] ESLint[123]
16.2 Chrome 插件
Vue.js devtools[124],你當(dāng)然要安裝支持 vue3 的版本,而且此版本對(duì) pinia 支持的也非常友好。
源碼
上述內(nèi)容,均可在我的開(kāi)源項(xiàng)目?X-BUILD[125]?中找到相關(guān)源碼,如果可以幫到你,請(qǐng)給一顆 star 或點(diǎn)贊鼓勵(lì)我貢獻(xiàn)出更多的開(kāi)源項(xiàng)目或文章。
參考
《基于Vue的前端架構(gòu),我做了這15點(diǎn)》[126] 《搭建自己的腳手架—“優(yōu)雅”生成前端工程》[127] 《Vuex4 對(duì) TypeScript 并不友好,所以我選擇 Pinia》[128] 《前端腳手架 webpack 遷移 Vite2 踩坑實(shí)踐》[129]
?? 看完兩件事
如果你覺(jué)得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我兩個(gè)小忙:
點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)
關(guān)注公眾號(hào)
