Vue-router 底层实现原理 源码实现 深度解析

Vue-router 底层原理

1、项目架构

在这里插入图片描述
以下文件夹,是我们实现路由原理所需的
boinnie-router文件夹里,是我们要自己实现的vue-router的源码
router文件夹里,是关于路由的配置
views文件夹,是我们存放单文件vue组件的地方
app.js 是我们的根组件
main.js 是我们将组件实现挂载到页面的地方

将文件夹和文件创建好之后,就开始实现vue-router的原理

2、代码解析步骤

第一步 先创建两个单文件组件Home和About

内容分别为
Home.vue

<template>
  <div class="home">
    <h1>This is an home page</h1>
  </div>
</template>

About.vue

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>

第二步 要写的文件是路由配置文件

router/index.js

import Vue from "vue";
import BonnieRouter from "../bonnie-router";

import Home from "../views/Home.vue";
import About from "../views/About.vue";

Vue.use(BonnieRouter);

const router = new BonnieRouter({
  routes: [
    {
      path: "/",
      component: Home
    },
    {
      path: "/about",
      component: About
    }
  ]
});
export default router;

代码解析:

  1. 需要将vue和vue-router底层实现的bonnie-router导入
  2. 导入自定义的单文件组件 Home、About
  3. 将bonnie-router挂载到Vue ,注意此处使用的Vue.use(BonnieRouter)是关键,后面将会进行讲解
  4. 配置路径和对应的组件
第三步 要写的文件是根组件App.vue

App.vue

<template>
  <div id="app">
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
    <router-view />
  </div>
</template>

将根组件挂载到页面,将路由挂载到vue实例
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");

第四步 vue-router底层源码实现

bonnie-router/index.js

let Vue;
class BonnieRouter {
//接收Vue.use()传递过来的vue
  static install(_Vue) {
    Vue = _Vue;
  }
  constructor(options) {
    //   接收传递过来的配置项
    this.$options = options;

    // 定义解析routes对象的变量
    this.routeMap = {};

    //  定义路由的地址  
    //这样写的目的是为数据驱动视图,数据改变时调用此处变量的地方也会做出相应变化
    this.app = new Vue({
      data: {
        curr: "/"
      }
    });
    //调用初始化方法
    this.init();
  }
	}
export default BonnieRouter;

前面说过 Vue.use(BonnieRouter) 是关键,在源码实现的这个文件里,使用了 一个静态方法 static install,使用的意义是为了接收前文Vue.use传递过来的Vue,因为在router/index.js里实例化BonnieRouter的时候传递了一个参数,所以在此文件里我们需要给构造函数添加一个参数options,然后我们需要调用一些初始化的方法,具体代码如下

init() {
    // 1.创建路由事件
    this.bindEvent();
    // 2.创建 路由对象
    this.createRouteMap();
    // 3.创建组件
    this.createComponent();
  }

显而易见,我们还需要实现其他的一些方法,首先实现bindEvent()方法。在这个方法中,我们给window绑定了两个事件,在页面加载load的时候和在路由hash发生变化的时候都去调用另一个方法handleChange

bindEvent(){
   //   hash模式路由

    //页面初始化时也调用handleChange方法
    window.addEventListener("load", this.handleChange.bind(this));

    //当路由发生改变时,执行handleChange方法
    window.addEventListener("hashchange", this.handleChange.bind(this));
}

然后实现handleChange方法,代码如下。这个方法的目的就是根据从url栏获取到的hash变化修改上文里 默认路由地址this.app.curr,若没有获取到hash变化,则赋值 ‘ / ’

handleChange() {
    // 更新视图
    // 1.先获取url栏里的hash
    // 2.将#去掉 替换 上文里  this.app.curr的值
    // 3.如果获取不到hash    默认赋值 /
    let hash = this.getHash() || "/";
    this.app.curr = hash;
  }
  //若果获取到url的hash,就会执行此方法
  getHash() {
    return location.hash.slice(1);
  }

然后我们就要实现createRouteMap()方法,创建路由对象的方法。在构造函数里接收的参数在此处进行使用,这里的optios其实就是router/index.js里的 路由配置项 ,我们将其遍历放进一个对象里。key就是path,vaule就是路由的每个配置项

createRouteMap() {
    this.$options.routes.forEach(item => {
      this.routeMap[item.path] = item;
    }); 
  }

最后我们要实现createComponent方法。此时,我们已经配置好的路由,然后就要创建两个标签router-link router-view,然后根据router-link的to属性来改变hash值,根据hash的变化来动态改变router-view要显示的组件

createComponent() {
    //   再此处创建 router-link router-view两个标签
    Vue.component("router-view", {
      render: h => {
        //h 是创建虚拟dom   格式要以  ↓  这种方式呈现\编写
        //return h("div", { attr: { "data-id": "0" } }, this.$solts.default);

        //但我们其实已经创建好了单文件组件,没必要在以上种方式去写  直接取出对应的组件就ok
        
        var component = this.routeMap[this.app.curr].component; // 根据curr的值动态获取component组件
        return h(component);
      }
    });

    Vue.component("router-link", {
      props: {
        //将router-link 标签上 to属性的值取出
        to: {
          type: String,
          required: true
        }
      },
      render: function(h) {
        return h(
          "a",
          {
            attrs: {
              href: `#${this.to}`
            }
          },
          this.$slots.default
        );
      }
    });
  }

router-link的实现讲解:
就是创建一个vue组件,组件名叫’ router-link ‘,这个组件是一个a标签,给他添加一个href属性,属性值就是调用的时候写的那个to属性,to属性是必填的
router-view的实现讲解:
还是创建一个vue组件,组件名叫’ router-view ',我们要根据router/index.js里路由的配置,即根据path来显示相应的component

路由的底层实现就是如此。
总结:先是根据url地址栏的变化来触发hashchange事件,此时会修改this.app.curr的值。然后会顺序执行createRouteMap方法,来根据router/index.js里路由的配置项来生成一个路由对象的集合。再执行createComponent方法来创建两个组件 router-link 、 router-view。
router-view是根据this.app.curr的值参照routerMap对象集合来显示相应的组件。
router-link的作用就是用来修改url地址栏,然后开始下个轮回

发布了13 篇原创文章 · 获赞 8 · 访问量 322

猜你喜欢

转载自blog.csdn.net/Dark_programmer/article/details/104477459