在使用vue spa时,配置路由是很重要的一环。众所周知nuxtjs中具备了依据pages文件自动生成vue-router 模块的路由配置。
此文将会引用nuxtjs中该部分源码,并稍加改动,生成一个vue-cli3的插件
第一部分
nuxt自动生成路由的使用方式
nuxt会根据pages下的文件自动生成路由并引入,支持vue-router的基础路由,动态路由,嵌套路由等。基础路由很简单,需要注意的是,在使用动态路由时,需要创建对应的以下划线作为前缀的 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'
}]
}
创建内嵌子路由,你需要添加一个 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作为参数传给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函数中对传过来的所有文件地址进行遍历,再对每一个文件地址字符串处理,以中划线进行拼接。以此作为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)
};
至此,经过这两个方法处理之后的内容,大致上就是我们想要的东西
//生成一个数组对象
[{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue'
}]
第二部分
vue-cli3插件
具体请移步vue-cli3插件开发指南
//插件目录
├── 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`
})
}
懒加载路由
当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。(详情请戳路由懒加载)
第三部分
自己动手做一个cli3插件
首先,关于插件的目录结构上面已经给出了,这里只需要service插件即可,而且暂时不需要读取webpack配置内容,只需要给出一个执行的契机
//插件目录
├── README.md
├── generateRouter
├── index.js # 插件方法
├── index.js # service 插件
└── package.json
const creatRouter = require('./generateRouter/index')
module.exports = (api, projectOptions) => {
// 这里只是需要一个时机执行,暂时不需要读取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中的内容
其中必要包含了前文中提到的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插件基本已经出炉,接下来只需要publish到npm上,然后使用vue add <your name>来加载插件即可
第四部分
使用方法
首先看一下cli3的项目目录结构,在使用了本插件之后,只需要在views中存放根组件(子组件存放于component中),router.js注册路由信息,当使用npm start时,将会根据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
}
]
以上便是本插件的全部内容,如果有疑问或者是bug可以联系我
qq:1053189708
git: https://github.com/zhangOking/vuecli3plugin-generatererouter
网友评论