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 相关文章推荐
javascript之更有效率的字符串替换
Aug 02 Javascript
javascript 异常处理使用总结
Jun 21 Javascript
jquery 日期控件datepicker属性详细解析
Nov 08 Javascript
jquery中$.post()方法的简单实例
Feb 04 Javascript
JS 在指定数组中随机取出N个不重复的数据
Jun 10 Javascript
JavaScript实现给按钮加上双重动作的方法
Aug 14 Javascript
Vue.js实现表格动态增加删除的方法(附源码下载)
Jan 20 Javascript
layer弹出层中H5播放器全屏出错的解决方法
Feb 21 Javascript
详解vue express启动数据服务
Jul 05 Javascript
ES6 javascript中class静态方法、属性与实例属性用法示例
Oct 30 Javascript
express+mockjs实现模拟后台数据发送功能
Jan 07 Javascript
深入理解Node内建模块和对象
Mar 12 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中空字符串介绍0、null、empty和false之间的关系
2012/09/25 PHP
php中mt_rand()随机数函数用法
2014/11/24 PHP
php将字符串随机分割成不同长度数组的方法
2015/06/01 PHP
php通过获取头信息判断图片类型的方法
2015/06/26 PHP
javascript Object与Function使用
2010/01/11 Javascript
javascript 拖动表格行实现代码
2011/05/05 Javascript
jquery数据验证插件(自制,简单,练手)实例代码
2013/10/24 Javascript
jQuery+PHP实现动态数字展示特效
2015/03/14 Javascript
使用jQuery实现更改默认alert框体
2015/04/13 Javascript
jQuery 操作input中radio的技巧
2016/07/18 Javascript
BootStrapValidator校验方式
2016/12/19 Javascript
ReactNative列表ListView的用法
2017/08/02 Javascript
详解Vue一个案例引发「内容分发slot」的最全总结
2018/12/02 Javascript
灵活使用console让js调试更简单的方法步骤
2019/04/23 Javascript
搭建一个Koa后端项目脚手架的方法步骤
2019/05/30 Javascript
微信小程序自定义组件实现环形进度条
2020/11/17 Javascript
vue改变循环遍历后的数据实例
2019/11/07 Javascript
[57:55]EG vs Fnatic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
python ElementTree 基本读操作示例
2009/04/09 Python
python动态加载变量示例分享
2014/02/17 Python
python获取远程图片大小和尺寸的方法
2015/03/26 Python
对python遍历文件夹中的所有jpg文件的实例详解
2018/12/08 Python
Python的形参和实参使用方式
2019/12/24 Python
windows10在visual studio2019下配置使用openCV4.3.0
2020/07/14 Python
Django如何批量创建Model
2020/09/01 Python
CSS3 linear-gradient线性渐变生成加号和减号的方法
2017/11/21 HTML / CSS
日本最大的眼镜购物网站:Oh My Glasses
2016/11/13 全球购物
Java工程师面试集锦之Spring框架
2013/06/16 面试题
软件测试英文面试题
2012/10/14 面试题
我们没有写servlet的构造方法,那么容器是怎么创建servlet的实例呢
2013/04/24 面试题
给老师的检讨书
2014/02/11 职场文书
双拥工作宣传标语
2014/06/26 职场文书
力克胡哲观后感
2015/06/10 职场文书
组织委员竞选稿
2015/11/21 职场文书
python使用pywinauto驱动微信客户端实现公众号爬虫
2021/05/19 Python
MySQL对数据表已有表进行分区表的实现
2021/11/01 MySQL