Vue Router 核心原理及实现
前言
本文主要介绍 Vue Router 的核心原理以及如何实现一个简易版本的 Vue Router(history 和 hash 模式)关于VueRouter的使用,此处不再赘述。
一. 前置知识
hash 和 history 模式区别
-
表现形式上:hash 模式 url https://localhost:8080?courseId=17#/detail?weekId=753&lessonId=3466, 带有 # 号。
-
原理区别:hash 模式基于锚点,以及 onhashchange 事件。 history 模式是基于 HTML5 中的 history 模式。history.pushState、replaceState 在 IE10 以后才支持,存在兼容性问题。push 会向服务器发送请求,使用 pushState 不会发送请求,但是会在浏览器端产生历史记录,形成客户端路由。
-
history 模式使用
- 该模式需要服务器端的支持。
- 单页应用中,服务端不存在 www.testurl.com/login,地址会返回 404,提示找不到页面。
- history 模式下前后端工作过程:history 模式下,刷新页面会向服务器进行网络请求,后端处理
- history 模式,需要将默认的 html 文件返回给前端,前端获取到文件后再根据路由自行处理。
二. Vue Router实现
1.实现思路
- 创建 VueRouter 插件,静态方法 install
- 判断插件是否已经被加载
- 当 Vue 加载的时候把传入的 router 对象挂载到 Vue 实例上(注意:只执行一次)
- 创建 VueRouter 类
- 初始化,options、routeMap、app(简化操作,创建 Vue 实例作为响应式数据记录当前路
径) - initRouteMap() 遍历所有路由信息,把组件和路由的映射记录到 routeMap 对象中
- 注册 popstate 事件,当路由地址发生变化,重新记录当前的路径
- 创建 router-link 和 router-view 组件
- 当路径改变的时候通过当前路径在 routerMap 对象中找到对应的组件,渲染 router-view
- 初始化,options、routeMap、app(简化操作,创建 Vue 实例作为响应式数据记录当前路
3.JavsScript版 类图
2.代码实现
let _Vue = null;
class VueRouter {
static install(Vue) {
//1 判断当前插件是否被安装
if (VueRouter.install.installed) {
return;
}
VueRouter.install.installed = true;
//2 把Vue的构造函数记录在全局
_Vue = Vue;
//3 把创建Vue的实例传入的router对象注入到Vue实例
// _Vue.prototype.$router = this.$options.router
_Vue.mixin({
beforeCreate() {
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router;
}
},
});
}
constructor(options) {
this.options = options;
this.routeMap = {};
// observable
this.data = _Vue.observable({
current: "/",
});
this.init();
}
init() {
this.createRouteMap();
this.initComponent(_Vue);
this.initEvent();
}
createRouteMap() {
//遍历所有的路由规则 吧路由规则解析成键值对的形式存储到routeMap中
this.options.routes.forEach((route) => {
this.routeMap[route.path] = route.component;
});
}
initComponent(Vue) {
Vue.component("router-link", {
props: {
to: String,
},
render(h) {
return h(
"a",
{
attrs: {
href: this.to,
},
on: {
click: this.clickhander,
},
},
[this.$slots.default]
);
},
methods: {
clickhander(e) {
history.pushState({}, "", this.to);
this.$router.data.current = this.to;
e.preventDefault();
},
},
// template:"<a :href='to'><slot></slot><>"
});
const self = this;
Vue.component("router-view", {
render(h) {
// self.data.current
const cm = self.routeMap[self.data.current];
return h(cm);
},
});
}
initEvent() {
//
window.addEventListener("popstate", () => {
this.data.current = window.location.pathname;
});
}
}