美文网首页让前端飞Web前端之路程序员
前端路由探究(1) vue-router源码学习-基本数据结构

前端路由探究(1) vue-router源码学习-基本数据结构

作者: kuulid | 来源:发表于2017-12-09 20:47 被阅读72次

    搭建实验环境

    首先github download下来https://github.com/vuejs/vue-router vue-router最新的代码,代码在src下,结构清晰,但不适合调试看中间的一些处理结果,所以新建一个文件夹,新建index.html,创建好vue-router官网上的demo实例

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <body>
        <div id="app">
            <h1>Hello App!</h1>
            <p>
              <!-- 使用 router-link 组件来导航. -->
              <!-- 通过传入 `to` 属性指定链接. -->
              <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
              <router-link to="/foo">Go to Foo</router-link>
              <router-link to="/bar">Go to Bar</router-link>
            </p>
            <!-- 路由出口 -->
            <!-- 路由匹配到的组件将渲染在这里 -->
            <router-view></router-view>
          </div>
    </body>
    <script src="./vue.js"></script>
    <script src="./vue-router.js"></script>
    <script>
        const Foo = { 
            template: '<div>foo</div>',
            created() {
                console.log('created');
            },
            methods: {
                hello() {
                    console.log('hello');
                }
            }
        }
        const Bar = { template: '<div>bar</div>' }
    
        const routes = [
            { path: '/foo', component: Foo },
            { path: '/bar', component: Bar }
        ]
    
        const router = new VueRouter({
            routes // (缩写)相当于 routes: routes
        })
    
        const app = new Vue({
            router
        }).$mount('#app')
    </script>
    </html>
    

    不同的是将官网上的CDN代码download下来到本地同文件夹下。
    github下载的工程文件比较好阅读,单文件的vue-router.js可以一步步调试看数据结构,两个结合起来看。

    matcher

    我们打开VueRouter对象代码,有两个地方值得关注,一个是

    this.matcher = createMatcher(options.routes || [], this);
    

    另一个

      switch (mode) {
        case 'history':
          this.history = new HTML5History(this, options.base);
          break
        case 'hash':
          this.history = new HashHistory(this, options.base, this.fallback);
          break
        case 'abstract':
          this.history = new AbstractHistory(this, options.base);
          break
        default:
          {
            assert(false, ("invalid mode: " + mode));
          }
      }
    

    可以看出一个是初始化matcher的createMatcher,打开createMatcher方法


    createMatcher

    我们的任务是看到vue-router是怎么处理存储路由结构的,所以应该是createRouteMap和addRoutes这个两个方法相关,但打开addRoutes,其实也是调用了createRouteMap,它是暴露给用户动态添加路由的方法。

    so ,看createRouteMap,这个方法有个循环routes调用addRouteRecord,最终生成每个路由的数据结构


    createRouteMap处理routers

    追寻到这个方法,我们找到了每个routes的record数据结构


    record结构
    其中regex是通过给出的路由生成对应的正则表达式,还有对应的components,addRouteRecord还考虑了子路由的处理过程。但最终record通过对象map放到pathMap
    pathMap

    我们可以通过console打印出来


    pathMap
    而最终createRouteMap返回出来的是, 即VueRouter中matcher对象的数据结构。
    createRouteMap返回结构

    History

    可以看到VueRouter如下生成History

      switch (mode) {
        case 'history':
          this.history = new HTML5History(this, options.base);
          break
        case 'hash':
          this.history = new HashHistory(this, options.base, this.fallback);
          break
        case 'abstract':
          this.history = new AbstractHistory(this, options.base);
          break
        default:
          {
            assert(false, ("invalid mode: " + mode));
          }
      }
    

    分别是vue-router三种模式,具体的分别就不介绍了,官网上有。但这三个对象都是继承了基本的History类。在vue-router工程文件src/history/base.js,可以看到这个类的定义


    History

    简单的来说,我们操作路由router.push({ path: 'home' }),其实是在操作这个对象上的方法,而各个模式下的具体操作不同,例如hash模式下需要监听popstate和hashchange事件,调用之前matcher的match函数(具体调用在History的transitionTo中),匹配并更新window.history。具体的操作都在src/history下。
    主要关注setupListeners -> transitionTo -> pushState这个几个方法的处理。可以看到最终操作window.history的是history.replaceState和history.pushState两个游览器API。

    总结

    通过matcher和History两个最重要的对象,VueRouter就完成了将我们定义好的路由结构转换成可以匹配的路由对象,并且添加事件监听,完成对相应路由的匹配并更新url历史。那么还有一个最重要的就是最终操作匹配到的组件并更新,这个下次写, 还有View和Link两个组件,其实也很简单,调用了Vue.util.defineReactive,定义了一个响应式对象:

    Vue.util.defineReactive(this, '_route', this._router.history.current)
    

    相关文章

      网友评论

        本文标题:前端路由探究(1) vue-router源码学习-基本数据结构

        本文链接:https://www.haomeiwen.com/subject/uotpixtx.html