深入了解vue-router原理并实现一个小demo

目录
  • 插件编写的基本方法
  • 需求分析
    • 我们先看看vue-router的使用步骤
    • 由此我们看看vue-router内部做了什么?
  • 实现思路
    • 首先我们看看如何将$router挂载到组件上​
    • ​如何实现那两个路由组件
      • 先来看看router-link
  • 完整demo代码
  • 总结

插件编写的基本方法

推荐大家先看看官方给出的插件使用和开发方法

https://vuejs.bootcss.com/guide/plugins.html​

需求分析

我们先看看vue-router的使用步骤

1.use

Vue.use(VueRouter)

注意️:

Vue.use()主要是调用插件内部的install方法,并将Vue实例作为参数传入​

2.new 一个router实例

const router = new VueRouter({
  // 实例化router传入的参数
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

3.new Vue() ,把实例放在vue的配置项里面

new Vue({
  router, // 注意router的实例也往里传
  render: h => h(App)
}).$mount('#app')

4.使用路由组件<router-view/><router-link></router-link>或者在组件中使用this.$router

由此我们看看vue-router内部做了什么?

将$router挂载到全局上实现并声明了两个组件:<router-view/><router-link></router-link>

实现思路

首先我们看看如何将$router挂载到组件上​

let Vue; // 保存vue的构造函数,避免打包将其打进去
VueRouter.install = function (_Vue) {
  Vue = _Vue;
  console.log("options", Vue.$options);
  Vue.mixin({
    beforeCreate() {
      console.log("inner", this);
      console.log(" this.$options.router", this.$options.router);
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router;
      }
    },
  });
  console.log("end");
};

可以看到:

1、第一次执行的时候,即在Vue.use(Router)时,还没有实例化vue(因为Vue.use()发生在 new Vue()之前),所以Vue.$option本身是拿不到的(ps: option就是new Vue()时传入的参数,router也往里面传),此时既然拿不到router的实例,所以不能直接在install方法里面挂载;

​2、我们可以在use的时候做一个全局混入,在合适的时间点,获取到Vue根实例配置项中的router实例, 执行挂载。紧接着在new Vue()根实例创建的时候,因为注入了router实例,所以再执行全局混入(mixin)中的生命周期时,这个时候根实例的配置项this.$options已经包含了router实例,可以此时把router挂载到Vue的原型上。之后所有Vue实例扩展来的VueCompont都可以通过this.$router访问到这个属性

​如何实现那两个路由组件

先看看路由组件如何使用

<div id="app">
  <div id="nav">
    <!-- a标签控制跳转 -->
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </div>
  <!-- 路由出口 -->
  <router-view />
</div>

由上面可以看出,点击router-link,就相当于点了a标签,然后a标签的href属性控制页面路由发生了变化;监听路由变化,然后仔router-view里面输出不同的模板;​

先来看看router-link

class VueRouter {
  constructor(options) {
    // 接受传入的参数
    this.$options = options;
    const initial = "/";
    // 将current变成响应式数据,
    //这样在hashchange的回掉中修改curent时,
    //用到current的router-view的render函数就会重新渲染
    Vue.util.defineReactive(this, "current", initial);
    // 监听路由变化
    window.addEventListener("hashchange", () => {
      // 获取当前url中的hash
      this.current = window.location.hash.slice(1);
    });
  }
}

VueRouter.install = function (_Vue) {
  Vue = _Vue;
  Vue.component("router-view", {
    render(h) {
      // 获取当前路由所对应的组件,然后把它渲染出来
      const { current, $options } = this.$router;
      // 这里要注意 我们传进来的routes是一个路由表,如下图一
      // 所以这里我们是找出匹配到当前current路由的项,然后直接渲染组件
      const route = $options.routes.find((item) => {
        return item.path === current;
      });
      let component = route ? route.component : null;

      return h(component);
    },
  });
}

​再来看看router-view

class VueRouter {
  constructor(options) {
    // 接受传入的参数
    this.$options = options;
    const initial = "/";
    // 将current变成响应式数据,
    //这样在hashchange的回掉中修改curent时,
    //用到current的router-view的render函数就会重新渲染
    Vue.util.defineReactive(this, "current", initial);
    // 监听路由变化
    window.addEventListener("hashchange", () => {
      // 获取当前url中的hash
      this.current = window.location.hash.slice(1);
    });
  }
}

VueRouter.install = function (_Vue) {
  Vue = _Vue;
  Vue.component("router-view", {
    render(h) {
      // 获取当前路由所对应的组件,然后把它渲染出来
      const { current, $options } = this.$router;
      // 这里要注意 我们传进来的routes是一个路由表,如下图一
      // 所以这里我们是找出匹配到当前current路由的项,然后直接渲染组件
      const route = $options.routes.find((item) => {
        return item.path === current;
      });
      let component = route ? route.component : null;

      return h(component);
    },
  });
}

图一

完整demo代码

// 我们要实现什么
// 1、插件
// 2、两个组件

// 保存vue的构造函数,避免打包将其打进去
let Vue;
class VueRouter {
  constructor(options) {
    this.$options = options;
    const initial = "/";
    Vue.util.defineReactive(this, "current", initial);
    this.current = "/";
    window.addEventListener("hashchange", () => {
      // 获取当前url中的hash
      this.current = window.location.hash.slice(1);
    });
  }
}

// 参数1在Vue.use()调用时传进来,
VueRouter.install = function (_Vue) {
  Vue = _Vue;
  console.log("options", this);

  // 全局混入
  // 目的:延迟下面的逻辑 到 router创建完毕并且附加到选项上时才执行
  Vue.mixin({
    // 在每个组件创建实例时都会执行
    beforeCreate() {
      // this.$options.router ;即new Vue时放进去的router实例
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router;
      }
    },
  });

  // 注册并且实现两个组件
  Vue.component("router-link", {
    props: {
      to: {
        required: true,
      },
    },
    render(h) {
      return h(
        "a",
        {
          attrs: { href: "#" + this.to },
        },
        this.$slots.default
      );
    },
  });
  Vue.component("router-view", {
    render(h) {
      // 获取当前路由所对应的组件,然后把它渲染出来
      const { current, $options } = this.$router;
      const route = $options.routes.find((item) => {
        return item.path === current;
      });
      let component = route ? route.component : null;

      return h(component);
    },
  });
};

export default VueRouter;

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

时间: 2022-03-02

浅析vue-router实现原理及两种模式

之前用Vue开发单页应用,发现不管路由怎么变化,浏览器地址栏总是会有一个'#'号. 当时检查自己的代码,没有发现请求的地址带'#',当时也很纳闷,但是由于没有影响页面的渲染以及向后台发送请求,当时也没有在意.最近看了一下vue-router的实现原理,才逐渐揭开了这个谜题. vue-router 的两种方式(浏览器环境下) 1. Hash (对应HashHistory) hash("#")符号的本来作用是加在URL中指示网页中的位置: http://www.example.com/in

详解Vue-Router源码分析路由实现原理

深入Vue-Router源码分析路由实现原理 使用Vue开发SPA应用,离不开vue-router,那么vue和vue-router是如何协作运行的呢,下面从使用的角度,大白话帮大家一步步梳理下vue-router的整个实现流程. 到发文时使用的版本是: - vue (v2.5.0) - vue-router (v3.0.1) 一.vue-router 源码结构 github 地址:https://github.com/vuejs/vue-router components下是两个组件<rout

浅析前端路由简介以及vue-router实现原理

路由这个概念最先是后端出现的.在以前用模板引擎开发页面时,经常会看到这样 http://www.xxx.com/login 大致流程可以看成这样: 浏览器发出请求 服务器监听到80端口(或443)有请求过来,并解析url路径 根据服务器的路由配置,返回相应信息(可以是 html 字串,也可以是 json 数据,图片等) 浏览器根据数据包的 Content-Type 来决定如何解析数据 简单来说路由就是用来跟后端服务器进行交互的一种方式,通过不同的路径,来请求不同的资源,请求不同的页面是路由的其中

浅谈Vue.js路由管理器 Vue Router

起步 HTML <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <div id="app"> <h1>Hello App!</h1> <p>

Vue Router 实现动态路由和常见问题及解决方法

个人理解:动态路由不同于常见的静态路由,可以根据不同的「因素」而改变站点路由列表.常见的动态路由大都是用来实现:多用户权限系统不同用户展示不同导航菜单. 如何利用Vue Router 实现动态路由 Vue项目实现动态路由的方式大体可分为两种: 前端将全部路由规定好,登录时根据用户角色权限来动态展示路由: 路由存储在数据库中,前端通过接口获取当前用户对应路由列表并进行渲染: 第一种方式在很多Vue UI Admin上都实现了,可以去读一下他们的源码理解具体的实现思路,这里就不过多展开.第二种方式现

vue router嵌套路由在history模式下刷新无法渲染页面问题的解决方法

解决vue-router嵌套路由(子路由)在history模式下刷新无法渲染页面的问题,具体内容如下 一. 异常描述 本来使用的是vue-router的hash模式,但是hash模式下url需要带"#"符号,不仅看起来不舒服,而且有些场景下是会破坏路由中的"#"(微信分享页面就会把"#"后边的内容处理掉),所以就需要使用history模式,然后就让后端改下nginx配置: location / { try_files $uri $uri/ /in

vue router动态路由设置参数可选问题

在日常工作中,我们需要将匹配到的所有路由,映射到一个组件上. 如下代码想要达到的效果: 不传page和id,则映射到user默认list页面 传page和id,根据page不同,显示不同的页面 问题 使用以下代码片段是不能实现以上效果的,因为默认情况下page和id参数是必传的,如果不传参数,则会根据默认路由跳转到home页面 new Router({ routes: [ { path: '/user/:page/:id', name: 'User', component: () => impo

vue router学习之动态路由和嵌套路由详解

本文主要参考:https://router.vuejs.org/zh-cn/essentials/nested-routes.html 本文的阅读前提是已经能够搭建一个vue前台程序并且运行.如果还么有搭建可以参考文章: http://www.jb51.net/article/111650.htm 好,下面上货. 首先介绍一下动态路由. 动态路由按照我的理解,就是说能够进行页面的跳转,比如说:下面的这个页面中: <template> <div id="app">

详解vue 单页应用(spa)前端路由实现原理

写在前面:通常 SPA 中前端路由有2种实现方式: window.history location.hash 下面就来介绍下这两种方式具体怎么实现的 一.history 1.history基本介绍 window.history 对象包含浏览器的历史,window.history 对象在编写时可不使用 window 这个前缀.history是实现SPA前端路由是一种主流方法,它有几个原始方法: history.back() - 与在浏览器点击后退按钮相同 history.forward() - 与

vue router动态路由下让每个子路由都是独立组件的解决方案

vue-router 之动态路由 vue-router官网上面是这样说的 // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }}) 然后,我就这样写了: this.$router.push({path:'manage', query: {id: 'tasklist'}})1 结果很明显,失败了.然后我就默默的再次看了一下官网,结果发现了这句话 // 命名的路由 r

vue+Vue Router多级侧导航切换路由(页面)的实现代码

当当当当当~我又来了. 在项目里经常会遇到侧导航切换页面的功能. 如果我们将侧导航做成公共组件,来调用的话,就会在每一个页面都引用该组件,在后期维护的时候比较麻烦,比如改参数. 所以此文将侧导航做成父页面组件,将切换的页面做成子页面,这样只需调用一次即可.大大减少了后期维护的麻烦 涉及功能点 侧导航支持多级 Vue Router的使用方法( 官方文档 ) 子父组件的写法 样式:elementUI 效果图 实现 --- 目录结构 --- Vue Router的使用方法 首先安装 npm insta

vue router 通过路由来实现切换头部标题功能

在做单页面应用程序时,一般页面布局头尾两块都是固定在布局页面,中间为是路由入口.这时访问页面时头部标题不会变,该问题的解决方案如下: 通过采用组件内路由卫士(beforeRouterEnter.beforeRouterUpdate)与路由元信息(meta) 来实现更新头部标题信息.点击查看文档 beforeRouterEnter:第一次进入时调用. beforeRouterUpdate:重复使用当前组件时调用. 效果图如下: 注意看页面标题与图标变换  路由元信息(meta)配置 在路由元信息中