1、内容
- 手写路由两种实现hashRouter及HistoryApi
- 手写VueRouter中嵌套路由
- 手写VueRouter中addRouteres原理
- 手写RouterLink及RouterView原理
- 手写VueRouter中钩子实现原理
- VueRouter3中VueRouter4对比

两种路由比较
spa 单页应用不刷新 但是要切换页面(组件)
hash路由: 好处 兼容性好, 缺点: 丑
history路由:兼容性不好 通过history.pushState 来进行跳转,跳转时去匹配组件 popState来监控
使用插件的时候 Vue内部会调用install方法将this(Vue)传入, 保证VueRouter内部不需要再传入Vue,保证内外Vue一致,

vue-router/index.js
import install from "./install"import createMatcher from "./create-matcher"import HashHistory from "./history/hash.js"import HTML5History from "./history/html5.js"class VueRouter {constructor(options){this.mode = options.mode;console.log(options)// 需要创建一个匹配器 (核心 匹配 动态添加)this.matcher = createMatcher(options.routes||[]);switch (this.mode) {case "hash":this.history = new HashHistory();;break;case "history":this.history = new HTML5History();break;}console.log(this.history)}init(app){ // 路由的初始化(1次)console.log("app根实例")console.log(this._routerRoot)console.log(this._router)// 希望根据历经来进行跳转对应的组件const history = this.history;// 此方法应该数据 base中的, 大家都有的const setupListener =()=>{history.setupListener(); // 美中方法监控的方式不一样}this.history.transitionTo(history.getCurrentLocation(),setupListener)}}VueRouter.install = installexport default VueRouter
vue-router/install.js
// 为了保证vueRouter和Vue 版本相同const install = function (Vue){// install的作用就是 将我们的routers实力共享给每个组件Vue.mixin({beforeCreate(){// 区分父子关系,先找父亲,儿子找父亲的属性,孙子找父亲// 组件生命周期顺序,先父后子if(this.$options.router){console.log('父','this.$options.name')// 将根实例放到_routerRoot上this._routerRoot = thisthis._router = this.$options.routerthis._router.init(this)}else{// 将根属性全部增加到每个组件的_routerRoot// 所有组件上都可以获取_routerRoot._router获取路由的实例this._routerRoot = this.$parent && this.$parent._routerRootconsole.log('子','this.$options.name')}}})}export default install;
vue-router/create-matcher.js
// 操作路由表function createMatcher(routes){console.log(routes)// 扁平化处理 一条路径一条记录let { pathMap } = createRouteMap(routes, oldPathMap);// matcher 1、原有路由基础上添加路由 2、根据路径匹配路由function addRoutes(){}function match(path){}return {addRoutes,match}}export default createMatcher
测试 —— test 添加动态路由,后面追加路由

create-route-map.js
function createRouteMap(routes,oldPathMap){let pathMap = oldPathMap || Object.create(null);// path => recoedroutes.forEach(route=>{addRouteRecord(route,pathMap)})return {pathMap}}function addRouteRecord(route,pathMap,parent){let path = parent ? `${parent.path}\${route.path}`: route.path;let record = {path:route.path,name:route.name,component:route.component}pathMap[path] = record// 如果有儿子就递归if(route.children){route.children.forEach(r=>{addRouteRecord(r,pathMap,record)})}}export default createRouteMap;

history/html5.js
import Base from "./base"class HTML5 extends Base {consturctor(router){super(router); // this.router = routergetCurrentLocation(){return window.location.pathname}setupListener(){// hash 值就是监控hash的变化window.addEventListener('popstate',()=>{this.transitionTo(this.getCurrentLocation())})}}export default HTML5;
history/hash.js
import Base from "./base"function ensureSlash(){if(window.location.hash){return}window.location.hash = '/'}class HASH extends Base {consturctor(router){super(router);ensureSlash() // 确保有HASH值getCurrentLocation(){return window.location.hash.slice(1)}setupListener(){// hash 值就是监控hash的变化window.addEventListener('hashchange',()=>{this.transitionTo(this.getCurrentLocation())})}}}export default HASH;
history/base.js
function transitionTo(history){constructor(router){this.router = router;}transitionTo(location,handler){// 根据路径,match出对应的记录handler && handler();}}

