無標(biāo)題文章

## 什么是路由自動注入

路由自動注入概念學(xué)習(xí)自[nuxt](https://zh.nuxtjs.org/guide/routing/),我們不需要在`router.js`中每次手動輸入代碼引入模塊而是自動根據(jù)`文件目錄格式`生成`router.js`

我們把這個功能獨(dú)立成一個`webpack`插件,并對相關(guān)功能進(jìn)行了完善,而且實(shí)現(xiàn)了`vue-router`的所有核心功能

更詳細(xì)使用指南和文檔可以查看我們的[github倉庫](https://github.com/Qymh/vue-router-invoke-webpack-plugin)

舉一個簡單的列子,比如你的目錄長這樣

```

src

├── views

│? ├── Login

│? │? └── Index.vue

│? └── User

│? ? ? ├── Account

│? ? ? │? └── Index.vue

│? ? ? ├── Home

│? ? ? │? └── Index.vue

│? ? ? └── Index.vue

```

規(guī)則很簡單,如果目錄的一層是`Index.vue`,則目錄名便是當(dāng)前的路由名字,如果是子文件夾則是第二層路由,之后自動生成的`router.js`會長成這樣

```javascript

{

? component: () =>

? ? import('@/views/Login/Index.vue'),

? name: 'login',

? path: '/login'

},

{

? component: () =>

? ? import('@/views/User/Index.vue'),

? name: 'user',

? path: '/user'

},

{

? component: () =>

? ? import('@/views/User/Account/Index.vue'),

? name: 'user-account',

? path: '/user/account'

},

{

? component: () =>

? ? import('@/views/User/Home/Index.vue'),

? name: 'user-home',

? path: '/user/home'

}

```

這里值得一提的是其實(shí)生成的`router.js`是沒有必要加入到版本控制當(dāng)中的,因?yàn)椴徽撛陂_發(fā)(`development`)還是生產(chǎn)(`production`)第一次構(gòu)建項(xiàng)目都會自動生成,比如你項(xiàng)目用到了`git`和`eslint`,那么應(yīng)該把它放在`.gitignore`和`.eslintignore`中

## 為什么使用路由自動注入

- 方便

不用每次去引用模塊,只用創(chuàng)建文件夾,`router.js`會自動生成

- 統(tǒng)一路由命名

![](https://user-gold-cdn.xitu.io/2019/4/12/16a10beb78c86c54?w=934&h=416&f=jpeg&s=66970)

如果有完整的`code review`這個問題是不會存在的,但我們稍微做了一點(diǎn)簡便,只要`code review`文件夾的命名就好了,最終生成的路由path會以駝峰命名,生成的name會以駝峰命名并且以連字符`-`連接不同層級的路由

- 統(tǒng)一路由層級

![](https://user-gold-cdn.xitu.io/2019/4/12/16a10c043d6e3568?w=840&h=202&f=jpeg&s=27927)

如圖片中的列子,我們無法從文件的命名去判斷路由到底在幾級,而且經(jīng)常寫的時候,明明是2級或3級路由卻和1級路由在一層路由下,這是很不規(guī)范而且與邏輯不符的

對比一下使用自動注入劃分層級后的路由

```javascript

src/views

├── Index.vue

├── NotFound.vue

├── Withdraw

? ? <!-- 第一級 -->

│? ├── Index.vue

│? └── Result

│? ? ? ├── Description

? ? ? ? ? ? <!-- 第三級 -->

│? ? ? │? └── Index.vue

? ? ? ? <!-- 第二級 -->

│? ? ? └── Index.vue

└── WithdrawHistory

? ? <!-- 第一級 -->

? ? └── Index.vue

```

可以從目錄結(jié)構(gòu)看出路由的層級

我們再來看看生成的路由,不同層級的路由名字通過連字符`-`連接,層級很清晰

```javascript

{

? ? component: () => import('@/views/Withdraw/Index.vue'),

? ? name: 'withdraw',

? ? path: '/withdraw'

},

{

? ? component: () => import('@/views/Withdraw/Result/Index.vue'),

? ? name: 'withdraw-result',

? ? path: '/withdraw/result'

},

{

? ? component: () => import('@/views/Withdraw/Result/Description/Index.vue'),

? ? name: 'withdraw-result-description',

? ? path: '/withdraw/result/description'

},

{

? ? component: () => import('@/views/WithdrawHistory/Index.vue'),

? ? name: 'withdrawHistory',

? ? path: '/withdrawHistory'

},

```

## 為什么選擇`vue-router-invoke-webpack-plugin`

- 完善的單元測試

![](https://user-gold-cdn.xitu.io/2019/4/15/16a1f183fdcf1541?w=1134&h=476&f=jpeg&s=103867)

- types支持

![](https://user-gold-cdn.xitu.io/2019/4/15/16a1f196e37e4ab3?w=1442&h=750&f=jpeg&s=161436)

## `vue-router-invoke-webpack-plugin`中獨(dú)特的路由劃分思維

當(dāng)我們的頁面過多的時候,比如項(xiàng)目有60多個甚至70多個單頁面,文件不可能會放在一個目錄下,一般這種時候,我們會按`功能`將相似功能的路由放在一個目錄下,我們之前也是這么做的,其實(shí)這么做也是沒啥問題的,但在路由自動注入下,我們提出了另外一種思路按路由`層級`劃分

什么是層級劃分呢,簡單的一句話就是根據(jù)頁面所在的相對url地址進(jìn)行劃分,舉個列子,我們的首頁如下

![](https://user-gold-cdn.xitu.io/2019/4/15/16a1f5fc22b5cc28?w=417&h=741&f=jpeg&s=41103)

首頁的路由為`/`,我們把首頁當(dāng)作根路由,那么可以進(jìn)入的一級路由分別為`提現(xiàn)` `提現(xiàn)記錄` `分成數(shù)據(jù)`等,點(diǎn)擊提現(xiàn)后,我們進(jìn)入了提現(xiàn)路由`/withdraw`

![](https://user-gold-cdn.xitu.io/2019/4/15/16a1f6320939c57e?w=442&h=769&f=jpeg&s=61984)

進(jìn)入提現(xiàn)頁面后,會有兩處可點(diǎn)擊,這兩處便是二級頁面,放在一級頁面的子文件夾中,按剛才的說法,路由目錄(截取部分)便是這樣

```

src/views

├── Bank

? ? <!-- 銀行卡管理 -->

│? └── Index.vue

├── DivideData

? ? <!-- 分成數(shù)據(jù) -->

│? └── Index.vue

<!- 首頁 --->

├── Index.vue

<!-- 404路由 -->

├── NotFound.vue

├── Withdraw

│? ├── BankDetails

? ? ? ? <!-- 提現(xiàn)中查看銀行卡信息 -->

│? │? └── Index.vue

│? ├── Description

? ? ? ? <!-- 提現(xiàn)說明 -->

│? │? └── Index.vue

? ? <!-- 提現(xiàn)頁面 -->

│? └── Index.vue

└── WithdrawHistory

? ? <!--提現(xiàn)記錄 -->

? ? └── Index.vue

```

其實(shí)一般這么分下來,相似功能的是會在一個文件夾下面的,也實(shí)現(xiàn)了按功能分路由的思路,而且這種層級劃分是一目了然的,很容易可以看出路由的從屬關(guān)系

但有時候也會遇到一個麻煩,就是有些頁面可能出現(xiàn)在當(dāng)前層級下面,也可能出現(xiàn)在另外一個層級下面,按功能分的時候也有這種,就是功能可能存在于兩個功能點(diǎn)之間,這種情況其實(shí)可以考慮下在哪個層級的權(quán)重重一點(diǎn)或者從用戶的點(diǎn)擊習(xí)慣考慮,哪個位置進(jìn)去會多一點(diǎn)就放在哪個層級下面

## `vue-router-invoke-webpack-plugin`中獨(dú)特的文件結(jié)構(gòu)

也許大家會有疑問,為啥非要寫成`Index.vue`并多加一層文件夾封裝,直接命名`vue`文件不好嗎,用過`nuxt`的同學(xué)可能也會感覺到這一點(diǎn)的區(qū)別,這也是我們在`nuxt`的基礎(chǔ)上增加的一個`feature`,為了更友好的封裝一個單頁面

舉個列子,如果你的項(xiàng)目沒有引用ui庫,很多業(yè)務(wù)組件需要自己寫,除了常用的組件會放在目錄最外面的`components`文件,其余的對應(yīng)一個單頁面的業(yè)務(wù)組件你會放在哪里呢,這就是我們預(yù)留的位置,比如一個目錄結(jié)構(gòu)如下

```

src/views

├── Audit

│? ├── Index.vue

│? ├── components

│? │? └── AuditItem.vue

│? └── images

│? ? ? └── AuditIntro.png

```

`Audit`是我們的審批頁面,其中用到了一個只有當(dāng)前頁面所用的`AuditItem.vue`組件,也引用了一個只有當(dāng)前頁面所用到的圖片`AuditIntro.png`,獨(dú)特的文件結(jié)構(gòu)就是為了這種需求而生的,當(dāng)前頁面的組件圖片放在一個文件夾中會更清晰,但值得一提的是,你也需要在插件中設(shè)置`ignore`去忽略掉不被我們解析的目錄,比如這樣

```javascript

plugins: [

? new VueRouterInvokeWebpackPlugin({

? ? dir: 'src/views',

? ? alias: '@/views',

? ? language: 'javascript',

? ? ignore: ['images', 'components', 'template.vue']

? })

];

```

那么 `images` `components` `template.vue` 會被忽略不解析

## 聊一聊路由權(quán)限控制

關(guān)于前端控制路由權(quán)限,前段時間看到過一個文章,感覺實(shí)現(xiàn)思路稍微復(fù)雜了點(diǎn),其實(shí)有一個比較簡單的思路,就是后端給定當(dāng)前用戶沒有權(quán)限的路由,然后前端在`beforeEach`鉤子中去匹配,如果匹配到?jīng)]有權(quán)限則直接跳404或者沒有權(quán)限的頁面就行了,如果用`vue-router-invoke-webpack-plugin`寫會這么寫

`apis.getForbiddenRoute`

```javascript

export default {

? // 請求當(dāng)前沒有權(quán)限的路由列表

? async getForbiddenRoute() {

? ? return ['/single/user'];

? }

};

```

```javascript

plugins: [

? ? new VueRouterInvokePlugin({

? ? ? // 觀察的目錄

? ? ? dir: 'demos/src',

? ? ? // 觀察目錄的別名

? ? ? alias: '@/src',

? ? ? // 當(dāng)前語言

? ? ? language: 'javascript',

? ? ? // 生成router.js的位置

? ? ? routerDir: 'demos',

? ? ? // 忽略文件夾

? ? ? ignore: ['images', 'template.vue', 'components', 'notfound.vue'],

? ? ? // 404路由地址

? ? ? notFound: '@/src/NotFound.vue',

? ? ? // 引用的模塊

? ? ? modules: [

? ? ? ? {

? ? ? ? ? name: 'apis',

? ? ? ? ? package: '@/apis'

? ? ? ? }

? ? ? ],

? ? ? // 同scrollBehavior

? ? ? scrollBehavior: (to, from, savedPosition) => {

? ? ? ? if (savedPosition) {

? ? ? ? ? return savedPosition;

? ? ? ? } else {

? ? ? ? ? return { x: 0, y: 0 };

? ? ? ? }

? ? ? },

? ? ? <!-- 主要是這段代碼 -->

? ? ? /* eslint-disable */

? ? ? beforeEach: async (to, from, next) => {

? ? ? ? // 通過綁定在靜態(tài)屬性上的_cachedForbiddenRoute判斷是否請求過接口

? ? ? ? if (!Vue._cachedForbiddenRoute) {

? ? ? ? ? Vue._cachedForbiddenRoute = [];

? ? ? ? ? await apis.getForbiddenRoute().then(res => {

? ? ? ? ? ? Vue._cachedForbiddenRoute = res;

? ? ? ? ? });

? ? ? ? }

? ? ? ? // 當(dāng)當(dāng)前頁面的地址存在于禁止訪問的列表中,則直接跳轉(zhuǎn)到404頁面

? ? ? ? if (Vue._cachedForbiddenRoute.includes(to.path)) {

? ? ? ? ? next({

? ? ? ? ? ? name: 'notFound'

? ? ? ? ? });

? ? ? ? } else {

? ? ? ? ? next();

? ? ? ? }

? ? ? }

? ? }),

]

```

但話說回來,任何實(shí)現(xiàn)思路,前端獲取的接口數(shù)據(jù)想篡改還是能繞過去的,所以還是得后端再防一層

## 項(xiàng)目實(shí)現(xiàn)思路

項(xiàng)目實(shí)現(xiàn)不太復(fù)雜,但要照顧到的地方很多

- 基本路由

- 動態(tài)路由

- 多層嵌套路由

- 多層嵌套動態(tài)路由

- meta替代品

- 文件不符合規(guī)則的友好處理

- 命名轉(zhuǎn)換統(tǒng)一

- node中原生`fs`模塊十分不友好

要考慮的小細(xì)節(jié)還挺多的,特別是當(dāng)路由過于復(fù)雜的情況

但node的`fs`的坑點(diǎn)是我沒有想到的,特別是在跨平臺上,所以我們舍棄了使用原生的`fs`模塊,用`chokidar`和`fs-extra`替代了`fs`的部分功能

前段時間也在學(xué)習(xí)`vue`的ast語法樹,所以學(xué)習(xí)了下思路去嘗試構(gòu)建一棵ast,不過方法還是有區(qū)別的,vue構(gòu)建語法樹是通過正則拆分了`元素開始標(biāo)簽` `元素屬性` `元素字符`? `元素結(jié)束標(biāo)簽`等然后拼接而成的,拼接的過程特別復(fù)雜,這個項(xiàng)目會簡單很多,直接通過文件讀取遞歸遍歷目錄就可以生成一棵`ast`了

![](https://user-gold-cdn.xitu.io/2019/4/15/16a1fa69ab9a7851?w=872&h=755&f=jpeg&s=100857)

然后通過語法樹去構(gòu)建字符串的`router.js`,構(gòu)建的過程還比較麻煩,最后將構(gòu)建好的字符串寫入文件就大功告成了

## 項(xiàng)目還需要完善的地方

- 單元測試

現(xiàn)在的單元測試覆蓋率已經(jīng)100%了,但我覺得仍然有比較多稍微復(fù)雜的情況沒有寫到,之后會不僅看單元測試覆蓋率,而是按想到需要測試得功能點(diǎn)去補(bǔ)充完整

- 測試環(huán)境

項(xiàng)目接入的是`circleci`,沒法在`windows`下測試,平常用的開發(fā)環(huán)境也是`mac`,所以測試環(huán)境方面之后還要去研究研究其他可以支持windows的ci工具,并對不同node版本進(jìn)行測試

其實(shí)現(xiàn)在在`windows`下也有一個bug,但我發(fā)現(xiàn)`nuxt`也有這個bug,所以感覺可能這不是一個bug或許是一個feature,之后也會去提一個`issue`去請教一下,也不知道是不是我電腦的問題,簡單說就是`fs.watch`去監(jiān)聽文件目錄的時候(但這里其實(shí)用的是`chokidar`,不過都一樣)當(dāng)去改變之前已有的文件目錄的名字是改不了的,`windows`下會提示你什么當(dāng)前文件被引用了,需要結(jié)束掉進(jìn)程這個文件名才能被修改

- 更友好的支持

項(xiàng)目目前支持的是node版本> 8.15.1,僅支持`webpack4`,之后會支持`webpack3`和即將到來的`webpack5`

2019-04-19 15:41:31 版本>0.2.5

已支持`webpack3`

## 更多的功能

除了剛才提到的一個簡單路由的列子和設(shè)置忽略項(xiàng),我們還支持了`vue-router`的其他核心功能,包括`動態(tài)路由` `嵌套路由` `全局路由守衛(wèi)` `meta替代品` 等其他功能,相關(guān)功能點(diǎn)都寫在了我們開源倉庫的文檔中,詳細(xì)的用法和注意事項(xiàng),可以訪問我們的[github倉庫](https://github.com/Qymh/vue-router-invoke-webpack-plugin),如果覺得項(xiàng)目還不錯的話,可以給我們點(diǎn)一顆小星星,當(dāng)然如果你在使用中發(fā)現(xiàn)了和預(yù)期不太一樣的情況或者bug可以隨時給我們提`issue`

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嵌灰,隨后出現(xiàn)的幾起案子弄匕,更是在濱河造成了極大的恐慌,老刑警劉巖沽瞭,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迁匠,死亡現(xiàn)場離奇詭異碗啄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)雕旨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門撵枢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人芙贫,你說我怎么就攤上這事搂鲫。” “怎么了磺平?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵魂仍,是天一觀的道長。 經(jīng)常有香客問我拣挪,道長擦酌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任菠劝,我火速辦了婚禮赊舶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赶诊。我一直安慰自己笼平,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布舔痪。 她就那樣靜靜地躺著寓调,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锄码。 梳的紋絲不亂的頭發(fā)上夺英,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機(jī)與錄音滋捶,去河邊找鬼痛悯。 笑死,一個胖子當(dāng)著我的面吹牛炬太,可吹牛的內(nèi)容都是我干的灸蟆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼亲族,長吁一口氣:“原來是場噩夢啊……” “哼炒考!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起霎迫,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤斋枢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后知给,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓤帚,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡描姚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了戈次。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轩勘。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怯邪,靈堂內(nèi)的尸體忽然破棺而出绊寻,到底是詐尸還是另有隱情,我是刑警寧澤悬秉,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布澄步,位于F島的核電站,受9級特大地震影響和泌,放射性物質(zhì)發(fā)生泄漏村缸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一武氓、第九天 我趴在偏房一處隱蔽的房頂上張望梯皿。 院中可真熱鬧,春花似錦聋丝、人聲如沸索烹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渊额,卻和暖如春况木,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背旬迹。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工火惊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奔垦。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓屹耐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親椿猎。 傳聞我的和親對象是個殘疾皇子惶岭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359

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