vue-router 源码之实现一个简单的 vue-router


Posted in Javascript onJuly 02, 2018

前言

通过上篇,我们知道前端理由的两种实现方法,Hash 路由与 History 路由,并且用它们分别实现了一个前端路由。

接下来我们就将 Vue 与 Hash 路由结合,实现一个非常简单的 vue-router 吧。

开始实现

想象一下,如果自己实现了一个 vue-router,会怎么去使用呢?参考 vue-router 官方的使用方式,看看 html 的使用:

<div id="app">
 <p>
  <router-link to="#/">home</router-link>
  <router-link to="#/book">book</router-link>
  <router-link to="#/movie">movie</router-link>
 </p>
 <router-view></router-view>
</div>

这里会有 router-link 和 router-view 两个组件需要我们来实现。再来看 js 的:

const Home = { template: '<div>home</div>' };
const Book = { template: '<div>book</div>' };
const Movie = { template: '<div>movie</div>' };

const routes = [
 { path: '/', component: Home },
 { path: '/book', component: Book },
 { path: '/movie', component: Movie }
];

const router = new VueRouter(Vue, {
 routes
});

new Vue({
 el: '#app'
});

这里会有我们自己定义的组件 Home、Book 和 Movie,并且有它们各自对应的路由。我们实现的 VueRouter 跟官方的有些区别,在 VueRouter 被 new 时是将 Vue 作为参数传入,而不是注入挂载到根实例下。

接下来就是 VueRouter 的实现了。

VueRouter

要怎么来实现 VueRouter 呢,先提供一下实现的思路:

  1. 绑定 hashchange 事件,实现前端路由;
  2. 将传入的路由和组件做一个路由映射,切换哪个路由即可找到对应的组件显示;
  3. 需要 new 一个 Vue 实例还做响应式通信,当路由改变的时候,router-view 会响应更新;
  4. 注册 router-link 和 router-view 组件。

先创建一个 VueRouter:

class VueRouter {
 constructor (Vue, options) {
  this.$options = options;
 }
}

绑定事件

给 VueRouter 添加一个绑定事件的方法,一旦路由发生改变,会触发 onHashChange 方法。

constructor (Vue, options) {
 this.init();
}

// 绑定事件
init () {
 window.addEventListener('load', this.onHashChange.bind(this), false);
 window.addEventListener('hashchange', this.onHashChange.bind(this), false);
}

路由映射表

将传入的 options 设置成一张路由映射表,以便于通过路由查找到对应的组件。

constructor (Vue, options) {
 this.$options = options;
 this.routeMap = {};
 this.createRouteMap(this.$options);
}

// 路由映射表
createRouteMap (options) {
 options.routes.forEach(item => {
  this.routeMap[item.path] = item.component;
 });
}

options 之中,路由与组件的关系:

const routes = [
 { path: '/', component: Home },
 { path: '/book', component: Book },
 { path: '/movie', component: Movie }
];

生成的路由映射表:

this.routeMap = {
 '/': Home,
 '/book': Book,
 '/movie': Movie
};

响应

我们需要 new 一个新的 Vue 实例,将当前路由 current 储存在其 data 之中,当修改了 current 时,router-view 就会自己去更新视图。

constructor (Vue, options) {
 this.app = new Vue({
  data: {
   current: '#/'
  }
 });
}

// 获取当前 hash 串
getHash () {
 return window.location.hash.slice(1) || '/';
}


// 设置当前路径
onHashChange () {
 this.app.current = this.getHash();
}

只要在 router-view 里使用到了 this.app.current,一旦更新它,便会更新。

注册组件

router-link 实际上就是一个 <a> 标签,点击它便能触发 hashchangerouter-view 会实现一个 render 方法,将当前路由对应的组件取出,进行渲染。

constructor (Vue, options) {
 this.initComponent(Vue);
}

// 注册组件
initComponent (Vue) {
 Vue.component('router-link', {
  props: {
   to: String
  },
  template: '<a :href="to" rel="external nofollow" rel="external nofollow" ><slot></slot></a>'
 });

 const _this = this;
 Vue.component('router-view', {
  render (h) {
   var component = _this.routeMap[_this.app.current];
   return h(component);
  }
 });
}

完整代码

至此,一个简单的 vue-router 就出来了,全部代码是这样的:

class VueRouter {
 constructor (Vue, options) {
  this.$options = options;
  this.routeMap = {};
  this.app = new Vue({
   data: {
    current: '#/'
   }
  });

  this.init();
  this.createRouteMap(this.$options);
  this.initComponent(Vue);
 }

 // 绑定事件
 init () {
  window.addEventListener('load', this.onHashChange.bind(this), false);
  window.addEventListener('hashchange', this.onHashChange.bind(this), false);
 }

 // 路由映射表
 createRouteMap (options) {
  options.routes.forEach(item => {
   this.routeMap[item.path] = item.component;
  });
 }

 // 注册组件
 initComponent (Vue) {
  Vue.component('router-link', {
   props: {
    to: String
   },
   template: '<a :href="to" rel="external nofollow" rel="external nofollow" ><slot></slot></a>'
  });

  const _this = this;
  Vue.component('router-view', {
   render (h) {
    var component = _this.routeMap[_this.app.current];
    return h(component);
   }
  });
 }

 // 获取当前 hash 串
 getHash () {
  return window.location.hash.slice(1) || '/';
 }

 // 设置当前路径
 onHashChange () {
  this.app.current = this.getHash();
 }
}

最后

将 Vue 与 Hash 路由结合,监听了 hashchange 事件,再通过 Vue 的 响应机制 和 组件,便有了上面实现好了一个 vue-router。

全部源码参考这里。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
&amp;lt;script defer&amp;gt; defer 是什么意思
May 10 Javascript
页面加载完成后再执行JS的jquery写法以及区别说明
Feb 22 Javascript
javascript中with()方法的语法格式及使用
Aug 04 Javascript
js动态切换图片的方法
Jan 20 Javascript
JS实现定时自动关闭DIV层提示框的方法
May 11 Javascript
基于jquery实现三级下拉菜单
May 10 Javascript
扩展Bootstrap Tooltip插件使其可交互的方法
Nov 07 Javascript
javascript使用btoa和atob来进行Base64转码和解码
Mar 20 Javascript
微信小程序页面渲染实现方法
Nov 06 Javascript
基于JS实现table导出Excel并保留样式
May 19 Javascript
js实现弹窗猜数字游戏
Nov 26 Javascript
Javascript实现单选框效果
Dec 09 Javascript
JavaScript设计模式之单例模式简单实例教程
Jul 02 #Javascript
JavaScript设计模式之建造者模式实例教程
Jul 02 #Javascript
JS实现的JSON序列化操作简单示例
Jul 02 #Javascript
JS内部事件机制之单线程原理
Jul 02 #Javascript
JS将网址url转化为JSON格式的方法
Jul 02 #Javascript
原生JS实现列表子元素顺序反转的方法分析
Jul 02 #Javascript
JS限制输入框输入的实现代码
Jul 02 #Javascript
You might like
PHP技术开发技巧分享
2010/03/23 PHP
mayfish 数据入库验证代码
2010/04/30 PHP
mac环境中使用brew安装php5.5.15
2014/08/18 PHP
php格式化json函数示例代码
2016/05/12 PHP
PHP实现的Redis多库选择功能单例类
2017/07/27 PHP
php 获取xml接口数据的处理方法
2018/05/31 PHP
Laravel 框架控制器 Controller原理与用法实例分析
2020/04/14 PHP
LBS blog sql注射漏洞[All version]-官方已有补丁
2007/08/26 Javascript
javascript向flash swf文件传递参数值注意细节
2012/12/11 Javascript
javascript判断chrome浏览器的方法
2014/03/26 Javascript
js拼接html注意问题示例探讨
2014/07/14 Javascript
取得元素的左和上偏移量的方法
2014/09/17 Javascript
jquery.serialize() 函数语法及简单实例
2016/07/08 Javascript
浅谈Vue的基本应用
2016/12/27 Javascript
javascript中mouseenter与mouseover的异同
2017/06/06 Javascript
vue数据双向绑定的注意点
2017/06/23 Javascript
JS设计模式之惰性模式(二)
2017/09/29 Javascript
vue-cli项目无法用本机IP访问的解决方法
2018/09/20 Javascript
详解vue-cli3多页应用改造
2019/06/04 Javascript
layui: layer.open加载窗体时出现遮罩层的解决方法
2019/09/26 Javascript
解决layui-table单元格设置为百分比在ie8下不能自适应的问题
2019/09/28 Javascript
vue自定义正在加载动画的例子
2019/11/14 Javascript
15分钟学会vue项目改造成SSR(小白教程)
2019/12/17 Javascript
Python实现根据IP地址和子网掩码算出网段的方法
2015/07/30 Python
python 中random模块的常用方法总结
2017/07/08 Python
python使用正则表达式替换匹配成功的组
2017/11/17 Python
python实现简单的购物程序代码实例
2020/03/03 Python
python多进程使用函数封装实例
2020/05/02 Python
pandas统计重复值次数的方法实现
2021/02/20 Python
Smashbox官网:美国知名彩妆品牌
2017/01/05 全球购物
如何用Python输出一个Fibonacci数列
2016/08/28 面试题
《美丽的黄昏》教学反思
2014/02/28 职场文书
护士毕业实习感言
2014/03/05 职场文书
2016见义勇为事迹材料汇总
2016/03/01 职场文书
辞职申请书范本
2019/05/20 职场文书
开机音效回归! Windows 11重新引入开机铃声
2021/11/21 数码科技