自己動手封裝一個vuecli3自動生成路由插件

在使用vue spa時,配置路由是很重要的一環(huán)啼器。眾所周知nuxtjs中具備了依據(jù)pages文件自動生成vue-router 模塊的路由配置躁劣。

此文將會引用nuxtjs中該部分源碼晃琳,并稍加改動拨匆,生成一個vue-cli3的插件

nuxt官方網(wǎng)站

第一部分

nuxt自動生成路由的使用方式

nuxt會根據(jù)pages下的文件自動生成路由并引入姆涩,支持vue-router的基礎(chǔ)路由,動態(tài)路由惭每,嵌套路由等阵面。基礎(chǔ)路由很簡單洪鸭,需要注意的是样刷,在使用動態(tài)路由時,需要創(chuàng)建對應(yīng)的以下劃線作為前綴的 Vue文件或目錄

//pages文件
pages/
--| users/
-----| _id.vue
--| index.vue
//生成的路由
router: {
  routes: [{
           name: 'index',
           path: '/',
           component: 'pages/index.vue'
          },
          {
           name: 'users-id',
           path: '/users/:id?',
           component: 'pages/users/_id.vue'
          }]
        }

創(chuàng)建內(nèi)嵌子路由览爵,你需要添加一個 Vue 文件置鼻,同時添加一個與該文件同名的目錄用來存放子視圖組件。

pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue
//生成的路由
router: {
  routes: [
    {
      path: '/users',
      component: 'pages/users.vue',
      children: [
        {
          path: '',
          component: 'pages/users/index.vue',
          name: 'users'
        },
        {
          path: ':id',
          component: 'pages/users/_id.vue',
          name: 'users-id'
        }
      ]
    }
  ]
}

nuxt源碼展示

源碼地址
分析一下nuxtjs源碼蜓竹,主要由以下幾部分完成此功能

  1. generateRoutesAndFiles方法箕母,引入glob庫對pages下的文件進行遍歷和字符串處理,然后將vue文件地址俱济,整個項目地址和pages作為參數(shù)傳給createRoutes方法
//builder.js
 else if (this._nuxtPages) {
      // Use nuxt.js createRoutes bases on pages/
      const files = {}
        ; (await glob(`${this.options.dir.pages}/**/*.{vue,js}`, {
        cwd: this.options.srcDir,
        ignore: this.options.ignore
      })).forEach((f) => {
        const key = f.replace(/\.(js|vue)$/, '');
        if (/\.vue$/.test(f) || !files[key]) {
          files[key] = f.replace(/('|")/g, '\\$1');
        }
      });
      templateVars.router.routes = common.createRoutes(
        Object.values(files),
        this.options.srcDir,
        this.options.dir.pages
      );
    }

2.在createRoutes函數(shù)中對傳過來的所有文件地址進行遍歷嘶是,再對每一個文件地址字符串處理,以中劃線進行拼接蛛碌。以此作為route.name

//common.js
const createRoutes = function createRoutes(files, srcDir, pagesDir) {
  const routes = [];
  files.forEach((file) => {
    const keys = file
      .replace(RegExp(`^${pagesDir}`), '')
      .replace(/\.(vue|js)$/, '')
      .replace(/\/{2,}/g, '/')
      .split('/')
      .slice(1);
    const route = { name: '', path: '', component: r(srcDir, file) };
    let parent = routes;
    keys.forEach((key, i) => {
      // remove underscore only, if its the prefix
      const sanitizedKey = key.startsWith('_') ? key.substr(1) : key;

      route.name = route.name
        ? route.name + '-' + sanitizedKey
        : sanitizedKey;
      route.name += key === '_' ? 'all' : '';
      route.chunkName = file.replace(/\.(vue|js)$/, '');
      const child = parent.find(parentRoute => parentRoute.name === route.name);

      if (child) {
        child.children = child.children || [];
        parent = child.children;
        route.path = '';
      } else if (key === 'index' && i + 1 === keys.length) {
        route.path += i > 0 ? '' : '/';
      } else {
        route.path += '/' + getRoutePathExtension(key);

        if (key.startsWith('_') && key.length > 1) {
          route.path += '?';
        }
      }
    });
    parent.push(route);
  });

  sortRoutes(routes);
  return cleanChildrenRoutes(routes)
};

至此聂喇,經(jīng)過這兩個方法處理之后的內(nèi)容,大致上就是我們想要的東西

//生成一個數(shù)組對象
[{
       name: 'index',
       path: '/',
       component: 'pages/index.vue'
  },
  {
       name: 'users-id',
       path: '/users/:id?',
       component: 'pages/users/_id.vue'
}]

第二部分

vue-cli3插件

具體請移步vue-cli3插件開發(fā)指南

//插件目錄
├── README.md
├── generator.js  # generator (可選)
├── prompts.js    # prompt 文件 (可選)
├── index.js      # service 插件
└── package.json
//index.js
module.exports = (api, projectOptions) => {
  api.chainWebpack(webpackConfig => {
    // 通過 webpack-chain 修改 webpack 配置
  })

  api.configureWebpack(webpackConfig => {
    // 修改 webpack 配置
    // 或返回通過 webpack-merge 合并的配置對象
  })

  api.registerCommand('test', args => {
    // 注冊 `vue-cli-service test`
  })
}

懶加載路由

當打包構(gòu)建應(yīng)用時蔚携,Javascript 包會變得非常大希太,影響頁面加載。如果我們能把不同路由對應(yīng)的組件分割成不同的代碼塊酝蜒,然后當路由被訪問的時候才加載對應(yīng)組件誊辉,這樣就更加高效了。(詳情請戳路由懶加載

第三部分

自己動手做一個cli3插件

首先亡脑,關(guān)于插件的目錄結(jié)構(gòu)上面已經(jīng)給出了堕澄,這里只需要service插件即可,而且暫時不需要讀取webpack配置內(nèi)容霉咨,只需要給出一個執(zhí)行的契機

//插件目錄
├── README.md
├── generateRouter 
        ├── index.js        # 插件方法 
├── index.js      # service 插件
└── package.json
const creatRouter = require('./generateRouter/index')

module.exports = (api, projectOptions) => {
// 這里只是需要一個時機執(zhí)行蛙紫,暫時不需要讀取webpack中的配置
    api.configureWebpack(webpackConfig => {
        creatRouter.creatRouter(false)
    })
}

package.json中的name要嚴格按照cli3的格式,要不然會出問題

//package.json
{
  "name": "vue-cli-plugin-<name>",
  "version": "1.0.0",
  "description": "your description",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "you",
  "license": "ISC",
  "dependencies": {},
  "devDependencies": {}
}

接下來看一看generateRouter/index中的內(nèi)容
其中必要包含了前文中提到的nuxtjs的方法躯护,只是對這兩個方法進行了一下改造

generateRoutesAndFiles = async () => {
    const files = {};
    // 這里要讀取的不是pages文件而是views文件了
    (await glob(`views/**/*.{vue,js}`, {
        cwd: path.resolve(process.cwd(), './src'),
        ignore: ['**/*.test.*', '**/*.spec.*', '**/-*.*']
    })).forEach((f) => {
        const key = f.replace(/\.(js|vue)$/, '')
        if (/\.vue$/.test(f) || !files[key]) {
            files[key] = f.replace(/('|")/g, '\\$1')
        }
    })

    return createRoutes(
        Object.values(files),
        path.resolve(process.cwd(), './src'),
        'views'
    )
}
function createRoutes(files, srcDir, pagesDir) {
    const routes = []
    const requireComponent = []
    files.forEach((file) => {
        const keys = file
            .replace(RegExp(`^${pagesDir}`), '')
            .replace(/\.(vue|js)$/, '')
            .replace(/\/{2,}/g, '/')
            .split('/')
            .slice(1)

        const route = {
            name: '',
            path: '',
            component: `views${camelCase(keys.join('-').replace('_', ''))}`
        }

        requireComponent.push(`const views${camelCase(keys.join('-').replace('_', ''))} = resolve => require(['../views/${keys.join('/')}'], resolve)`)
        let parent = routes
        keys.forEach((key, i) => {
            route.name = key.startsWith('_') ? key.substr(1) : key
            route.name += key === '_' ? 'all' : ''
            const child = parent.find(parentRoute => parentRoute.name === route.name)
            if (child) {
                child.children = child.children || []
                parent = child.children
                route.path = ''
            } else if (key === 'index' && i + 1 === keys.length) {
                route.path += i > 0 ? '' : '/'
            } else {
                route.path = `/` + getRoutePathExtension(key)

                if (key.startsWith('_') && key.length > 1) {
                    route.path += '?'
                }
            }
        })
        parent.push(route)
    })
    sortRoutes(routes)
    return {
        'routes': cleanChildrenRoutes(routes),
        'requireComponent': requireComponent
    }
}

一個cli3插件基本已經(jīng)出爐惊来,接下來只需要publish到npm上,然后使用vue add <your name>來加載插件即可

第四部分

使用方法

首先看一下cli3的項目目錄結(jié)構(gòu)棺滞,在使用了本插件之后裁蚁,只需要在views中存放根組件(子組件存放于component中),router.js注冊路由信息继准,當使用npm start時枉证,將會根據(jù)views中的文件自動生成router/route.js文件

|—node-modules
|—public
|—src
    |—assets
    |—component
    |—views
    |—router
         |—route.js
    |—router.js
    |—App.vue
    |—main.js
|—package.json

// 在router.js中注冊
import Vue from 'vue'
import Router from 'vue-router'
import {routes} from './router/route'

Vue.use(Router)

export default new Router({
  routes: routes
})

以下是生成出來的樣例

//router/route.js
const viewsAbout = resolve => require(['../views/About'], resolve)
const viewsHome = resolve => require(['../views/Home'], resolve)
const viewsuserUser = resolve => require(['../views/user/_user'], resolve)
const viewsuserUsernameUsername = resolve => require(['../views/user/username/username'], resolve)
export const routes = [
  {
    name: "About",
    path: "/About",
    component: viewsAbout
  },
  {
    name: "Home",
    path: "/Home",
    component: viewsHome
  },
  {
    name: "user-username-username",
    path: "/user/username/username",
    component: viewsuserUsernameUsername
  },
  {
    name: "user-user",
    path: "/user/:user?",
    component: viewsuserUser
  }
]

以上便是本插件的全部內(nèi)容,如果有疑問或者是bug可以聯(lián)系我
qq:1053189708
git: https://github.com/zhangOking/vuecli3plugin-generatererouter

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末移必,一起剝皮案震驚了整個濱河市室谚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崔泵,老刑警劉巖秒赤,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異憎瘸,居然都是意外死亡入篮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門幌甘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來潮售,“玉大人,你說我怎么就攤上這事锅风∷址蹋” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵皱埠,是天一觀的道長肮帐。 經(jīng)常有香客問我,道長边器,這世上最難降的妖魔是什么泪姨? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮饰抒,結(jié)果婚禮上肮砾,老公的妹妹穿的比我還像新娘。我一直安慰自己袋坑,他們只是感情好仗处,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著枣宫,像睡著了一般婆誓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上也颤,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天洋幻,我揣著相機與錄音,去河邊找鬼翅娶。 笑死文留,一個胖子當著我的面吹牛好唯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播燥翅,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼骑篙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了森书?” 一聲冷哼從身側(cè)響起靶端,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凛膏,沒想到半個月后杨名,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡猖毫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年台谍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鄙麦。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡典唇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胯府,到底是詐尸還是另有隱情介衔,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布骂因,位于F島的核電站炎咖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏寒波。R本人自食惡果不足惜乘盼,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俄烁。 院中可真熱鬧绸栅,春花似錦、人聲如沸页屠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辰企。三九已至风纠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牢贸,已是汗流浹背竹观。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人臭增。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓懂酱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親速址。 傳聞我的和親對象是個殘疾皇子玩焰,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

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