今天刚好有时间,最近也在观察vue3新特性,抽空玩一玩嵌套路由的vue-router,直接上代码
项目目录结构
代码展示
- app.vue
<template>
<div id="app">
<div>
<router-link to="/">Index</router-link> |
<router-link to="/person">Person</router-link> |
<router-link to="/person/info">PersonInfo</router-link>
</div>
<!-- 一级路由 -->
<router-view />
</div>
</template>
<style>
#app{
display: flex;
flex-direction: column;
align-items: center;
}
</style>
- Index.vue
<template>
<div class="index">
<h1>this is index page</h1>
</div>
</template>
- Person.vue
<template>
<div class="person">
<h1>this is person page</h1>
<!-- 二级路由 -->
<router-view />
</div>
</template>
- PersonInfo.vue
<template>
<div class="personInfo">
<h2>this is personInfo page</h2>
</div>
</template>
js文件
- main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
- babel.config.js
- 需要添加babel依赖支持新语法,如可选链
npm install --save-dev @babel/core @babel/cli
npm install --save-dev @babel/plugin-proposal-optional-chaining
module.exports = {
presets: [
'@babel/preset-env'
],
plugins: ['@babel/plugin-proposal-optional-chaining']
}
- router目录下文件
- index.js
import Vue from "vue";
import VueRouter from "./vue-router";
import Index from "../views/Index.vue";
import Person from "../views/Person.vue";
import PersonInfo from "../views/PersonInfo.vue";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "Index",
component: Index
},
{
path: "/person",
name: "Person",
component: Person,
children:[
{
path: "/person/info",
name: "PersonInfo",
component: PersonInfo
}
]
}
];
const router = new VueRouter({
routes
});
export default router;
- vue-router.js
这里先借助Vue的工具Vue.util.defineReactive
实现数据响应式,后续再手撕这个库
import routerLink from "./router-link";
import routerView from "./router-view";
let Vue;
class VueRouter {
constructor(options) {
this.$options = options
this.current = window.location.hash.slice(1) || "/"
// 设置响应式数组数据
Vue.util.defineReactive(this, "routerArray", [])
// 监听hash值变化
window.addEventListener("hashchange", this.hashChange.bind(this))
this.getRouterArray()
}
hashChange() {
this.current = window.location.hash.slice(1) || "/"
this.routerArray = []
this.getRouterArray()
}
getRouterArray(routes) {
routes = routes || this.$options.routes
for (const route of routes) {
if (this.current === '/' && route.path === '/') {
this.routerArray.push(route)
return
}
if (this.current.indexOf(route.path) !== -1 && route.path !== '/') {
this.routerArray.push(route)
if (route.children) {
// 递归子路由
this.getRouterArray(route.children)
}
return
}
}
}
}
VueRouter.install = function(_Vue) {
Vue = _Vue
// Vue全局混入,等new Vue中的router实例创建之后挂载到Vue上
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
Vue.prototype.$router = this.$options.router
}
},
});
// 注册router-link和router-view全局组件
Vue.component("router-link", routerLink)
Vue.component("router-view", routerView)
}
export default VueRouter
- router-link.js
export default {
props: {
to: {
type: String,
required: true
}
},
render(h) {
return h(
"a",
{
attrs: {
href: "#" + this.to,
},
},
this.$slots.default
);
}
};
- router-view.js
export default {
render(h) {
// 设置嵌套路由标识
this.$vnode.data.rv = true
// 嵌套路由设置深度
let depth = 0
let parent = this.$parent
while (parent) {
// 上级还有嵌套路由标识rv为true的,深度加一
if (parent.$vnode?.data?.rv) {
depth++
}
parent = parent.$parent
}
// 简单处理
const route = this.$router.routerArray[depth]
return h(route?.component);
}
};
-
效果图
好了,今天就玩到这里了,下次再玩别的哈