阅读 234

vue-router源码分析(一)

1. 先分析下 vue-router 引用方式

import VueRouter from 'vue-router'
Vue.use(VueRouter)
复制代码

发现是Vue.use(VueRouter)引入。 Vue.use 会加载 当前对象的install方法, 所以VueRouter提供了一个install方法

// router.js
const routes = [
  ...
  {
    path: '/',
    name: 'Home',
    component: Home
  }
  ...
]

const router = new VueRouter({
  routes
})

export default router

// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
复制代码

注入到根实例上

2. 用vue-cli创建工程,

    1. vue create my-vue-router(项目名)
    1. 选择以下配置

    image.png

    1. 创建完项目后,新增及更改文件操作
    • 3-1:新增 vue-router文件(自己写的vue-router文件目录)

    image.png

    • 3-2:更改 router/index.js, VueRouter替换成自己写的

    image.png

这里跑起来会报错,不用急, 代码还没开始写呢

3. 开始手写vue-router

    1. 从 src/vue-router/index.js 入口开始
    import install from './install';
    export default class VueRouter {
    
    }
    VueRouter.install = install;
    复制代码
    Vue.use 会加载 当前对象的install方法,所以给VueRouter类提供个install方法
    1. 新建 src/vue-router/install.js
    // 安装这个插件, 这个插件应该依赖于Vue
     export let _Vue;
     export default function install(Vue) { // Vue就是vue的构造函数, 外部Vue.use调用install时,会传入
         _Vue = Vue;
     }
    复制代码
    参数Vue就是vue的构造函数, 外部Vue.use调用install时,会传入

4. 注入全局router属性

  • src/vue-router/install.js

    现在只有main.js能获取到router, 所以需要通过Vue.mixin全局注入各个组件,具体代码及注释看以下:

    // 安装这个插件, 这个插件应该依赖于Vue
    export let _Vue;
    export default function install(Vue) { // Vue就是vue的构造函数, 外部Vue.use调用install时,会传入
       _Vue = Vue;
       // Vue,mixin 主要做了一件事:在所有组件上都增加了 _routerRoot 属性 
       // 通过Vue.mixin全局注入。 因为父子组件加载顺序是:父-beforeCreate => 子beforeCreate
       Vue.mixin({
         beforeCreate() {
             if (this.$options.router) {  // 一开始只有根组件有router(main.js)
                 this._routerRoot = this;
                 this._rooter = this.$options.router;
             } else {
                 // 获取父组件的_routerRoot,然后赋值给自己
                 this._routerRoot = this.$parent && this.$parent._routerRoot;
             }
         }  
       })
    }
    复制代码

5. 初始化方法

  • 5-1. 初始化方法,传入根实例
// src/vue-router/install.js
export let _Vue;
export default function install(Vue) { 
   _Vue = Vue;
   Vue.mixin({
     beforeCreate() {
         if (this.$options.router) {  
             this._routerRoot = this;
             this._rooter = this.$options.router;

            //  init()
            this._router,init(this) //初始化方法,传入根实例
         } else {
            this._routerRoot = this.$parent && this.$parent._routerRoot;
         }
     }  
   })
复制代码
  • 5-2. VueRouter初始化代码实现
    • 新建 src/vue-router/create-matcher.js
    // src/vue-router/index.js
    import install from './install';
    export default class VueRouter {
         constructor(options) {
             // createMatcher返回 match和addRoutes 
            // match 负责匹配路径{'/':'记录', 'about': '记录'}
            // addRoutes 动态添加路由配置
            this.matcher = createMatcher(options.routes || [])
        }
        init(app) { //app指代根实例
    
        }
    }
    VueRouter.install = install;
    复制代码
    // src/vue-router/create-matcher.js
    import createRouteMap from "./create-route-map";
    export default function createMatcher(routes) {
        // routes 外部用户当前传入的配置
        // 扁平化用户传入的数据,创建路由映射表
    
        // [/,/about/,/about/a, /about/b]
        // {/:'记录',/about:'记录', /about/a: '记录', /about/b: '记录'}
        let { pathList, pathMap } = createRouteMap(routes); //初始化配置
        // 动态添加的方法
        function addRoutes(routes) {  //添加新的配置
            createRouteMap(routes, pathList, pathMap)
        }
    
        // 匹配方法
        function match(location) { 
            
        }
    
        return {
            match,
            addRoutes    
        }
    }
    复制代码
    • 新建 src/vue-router/create-route-map.js
    export default function createRouteMap(routes, oldPathList, oldPathMap) {
        // 将用户传入的数据 进行格式话
        let pathList = oldPathList || [];
        let pathMap = oldPathMap || Object.create(null);
        routes.forEach(route => {
            addRouteRecord(route, pathList, pathMap);
        }) 
        console.log(pathList,pathMap)
        return {
            pathList,
            pathMap
        }   
    }
    
    function addRouteRecord(route, pathList, pathMap, parent) {
        let path = parent ? `${parent.path}/${route.path}` : route.path;
        let record = { //记录
            path,
            component: route.component,
            parent
        }
        if (!pathMap[path]) {
            pathList.push(path); // 将路径添加到pathList中
            pathMap[path] = record; 
        }
        if (route.children) {
            route.children.forEach(child => {
                addRouteRecord(child, pathList, pathMap, record)
            })
        }
    }
    复制代码

6. 创建路由系统

  • 6-1. 根据模式 创建不同的路由对象
...
import HashHistory from "./history/hash";
export default class VueRouter {
    constructor(options) {
        ...
        // 创建路由系统 根据模式 创建不同的路由对象    
        this.mode = options.mode || 'hash';
        this.history = new HashHistory(this);
    }
    init(app) { //app指代根实例

       
    }
    ...
}
VueRouter.install = install;
复制代码
  • 6-2. 新建文件夹src/vue-router/history/base.js 及 hash.js(本章只实现hash模式路由)
// src/vue-router/history/base.js
export default class History {
    constructor(router) {
        this.router = router;
    }
}    

// src/vue-router/history/hash.js
import History from './base';
export default class HashHistory extends History {
    constructor(router) {
        super(router);
    }
}    
    
复制代码
  • 6-3. 初始化
// src/vue-router/index.js
import HashHistory from "./history/hash";
export default class VueRouter {
    constructor(options) {
        ...
        // 创建路由系统 根据模式 创建不同的路由对象    
        this.mode = options.mode || 'hash';
        this.history = new HashHistory(this);
    }
    init(app) { //app指代根实例
        // 初始化: 先根据当前路径 显示到指定的 组件
        const history = this.history;
        const setupHashListener =  ()=> {
            history.setupListener();
        }
        history.transitionTo(
            history.getCurrentLocation(), // 后续要监听路径变化
            setupHashListener
        );    
    }
    match (location) {
        return this.matcher.match(location)
    }
    ...
}
VueRouter.install = install;
复制代码
// src/vue-router/history/base.js
export default class History {
    constructor(router) {
        this.router = router;
    }
    transitionTo(location, onComplete){

        let route = this.router.match(location);
        console.log('route',route)
        if (this.current.path === location && route.matched.length === 
            this.current.matched.length) {
                return;
            }
        onComplete && onComplete();
    }
}
复制代码

7. 我太难了,直接附上 完整代码 地址

github.com/glihui/vue-…

代码有相应的注释

该文章只实现了vue-router核心代码

8.最后:分享下自己整理的部分知识点文章链接

文章分类
前端
文章标签