在传统前端开发中,通常会在 src 下维护两个文件夹:
- view:存放视图 vue 文件
- router:存放路由模块 js 文件
但是我在工作中总感觉怪怪的,因为这两个东西存在太多相似的地方,甚至有些项目的这两个文件夹的结构存在一一对应的关系,增加一个视图页面就要增加一个路由,减少一个视图也要减少一个路由。
倘若项目规模变大后,同时维护这两个文件夹将变得越来越困难。
所以我想通过 require.context 机制根据 view 的目录结构动态生成一个对应的 VueRouter 对象,这样就不用在改变视图的时候再去修改路由了。下面是我的源码:
// main.js
import Vue from 'vue'
import view from './view'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
Vue.config.productionTip = false
function handleKey(ctx, key, mode) {
let fullpath
if (mode == 0) {
fullpath = key.substring(2, key.length-10)
} else {
fullpath = key.substring(2, key.length-4)
}
let cut = fullpath.split("/")
if (cut.length == 1) {
routes.push(createRoute(ctx(key), "/"+fullpath))
} else {
let parent = routes.find(obj => {
if (obj.path == "/"+cut[0]) return obj
})
for (let i = 1; i < cut.length-1; i++) {
parent = parent.find(obj => {
if (obj.path == cut[i]) return obj
})
}
if (parent.children == undefined) parent.children = []
parent.children.push(createRoute(ctx(key), cut[cut.length-1]))
}
}
function createRoute(component, path) {
let route = {}
if (component.route !== undefined) {
route = component.route
}
route.path = path
route.component = component.default
return route
}
const routes = []
// index vue
const indexvue = require.context("./view", true, /\/index\.vue$/)
indexvue.keys().sort().forEach(key => {
if (key === "./index.vue") return
handleKey(indexvue, key, 0)
})
// other vue
const othervue = require.context("./view", true, /\.vue$/)
othervue.keys().forEach(key => {
if (/\/index\.vue$/.test(key)) return
handleKey(othervue, key, 1)
})
const router = new VueRouter({
mode: 'history',
routes
})
new Vue({
router,
render: h => h(view),
}).$mount('#app')
为了使上面的程序良好运行,需要遵循几个要求:
- view 目录下的 index.vue 是整个项目的入口,记得在里面加上 id="app"
- 在 view 目录及子目录中,存在对应的 index.vue 视图,该视图通常被用来渲染同目录下的非 index 视图,所以需要加上 <router-view/>,该视图对应的自动生成的路由为相对 view 目录的路径,比如,要访问 view/path1/path2/index.vue ,对应的路由为 /path1/path2
- 对于非 index 视图,其自动生成的路由为相对路径加上文件名,比如,要访问 view/path1/path2/hello.vue,对应的路由就是 /path1/path2/hello
- 自动生成的路由只包含 path 和 component 属性,如果有其他属性需求,可以在 vue 文件内部暴露一个 route 对象,这样我觉得更好维护,比如:
<template>...</template>
<script>
export default {
...
}
export let route = {
redirect: "xx"
}
这种自动生成的路由也有一定的局限性,它不支持动态路由参数,只支持哪些路由和文件夹结构一一对应的项目。
源码可能会有 bug ,如有需要可以交流一下。