在使用vue spa時,配置路由是很重要的一環(huán)啼器。眾所周知nuxtjs中具備了依據(jù)pages文件自動生成vue-router 模塊的路由配置躁劣。
此文將會引用nuxtjs中該部分源碼晃琳,并稍加改動拨匆,生成一個vue-cli3的插件
第一部分
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源碼蜓竹,主要由以下幾部分完成此功能
- 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