手写 vue-router 核心原理
- 2022-12-07 浙江
本文字数:2065 字
阅读完需:约 7 分钟
最近也在观察 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);
}
};
复制代码
效果图
好了,今天就玩到这里了,下次再玩别的哈
划线
评论
复制
发布于: 刚刚阅读数: 5
还未添加个人签名 2022-09-08 加入
还未添加个人简介
评论