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 相关文章推荐
Mootools 图片展示插件(lightbox,ImageMenu)收集集合
May 21 Javascript
JavaScript事件处理器中的event参数使用介绍
May 24 Javascript
js控制web打印(局部打印)方法整理
May 29 Javascript
关于Javascript与iframe的那些事儿
Jul 04 Javascript
JavaScript中的Array 对象(数组对象)
Jun 02 Javascript
AngularJS 指令详细介绍
Jul 27 Javascript
JavaScript自定义分页样式
Jan 17 Javascript
使用vue.js实现checkbox的全选和多个的删除功能
Feb 17 Javascript
Angular.js中处理页面闪烁的方法详解
Mar 09 Javascript
jQuery中图片展示插件highslide.js的简单dom
Apr 22 jQuery
详解webpack4之splitchunksPlugin代码包分拆
Dec 04 Javascript
一篇文章了解正则表达式的替换技巧
Feb 24 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
Laravel框架表单验证操作实例分析
2019/09/30 PHP
laravel 修改记住我功能的cookie保存时间的方法
2019/10/14 PHP
重定向实现代码
2006/11/20 Javascript
在textarea中显示html页面的javascript代码
2007/04/20 Javascript
javascript  Error 对象 错误处理
2008/05/18 Javascript
javascript 折半查找字符在数组中的位置(有序列表)
2010/12/09 Javascript
JavaScript之IE的fireEvent方法详细解析
2013/11/20 Javascript
JQuery插件jcarousellite的参数中文说明
2015/05/11 Javascript
jquery获取url参数及url加参数的方法
2015/10/26 Javascript
JS实现alert中显示换行的方法
2015/12/17 Javascript
浅析$.getJSON异步请求和同步请求
2016/06/06 Javascript
微信小程序 require机制详解及实例代码
2016/12/14 Javascript
十分钟带你快速了解React16新特性
2017/11/10 Javascript
vue下history模式刷新后404错误解决方法
2018/08/18 Javascript
socket在egg中的使用实例代码详解
2019/05/30 Javascript
浅谈v-for 和 v-if 并用时筛选条件方法
2019/11/07 Javascript
layui实现显示数据表格、搜索和修改功能示例
2020/06/03 Javascript
[04:47]DOTA2-潍坊风行电子俱乐部探秘
2014/08/08 DOTA
python使用paramiko模块实现ssh远程登陆上传文件并执行
2014/01/27 Python
Python的净值数据接口调用示例分享
2016/03/15 Python
详解Python nose单元测试框架的安装与使用
2017/12/20 Python
python Spyder界面无法打开的解决方法
2018/04/27 Python
Pandas中Series和DataFrame的索引实现
2019/06/27 Python
python加载自定义词典实例
2019/12/06 Python
python json 递归打印所有json子节点信息的例子
2020/02/27 Python
简单了解pytest测试框架setup和tearDown
2020/04/14 Python
Keras模型转成tensorflow的.pb操作
2020/07/06 Python
python爬虫用scrapy获取影片的实例分析
2020/11/23 Python
世界上最大的街头服饰网站:Karmaloop
2017/02/04 全球购物
财务助理岗位职责
2013/11/10 职场文书
学生保证书范文
2014/04/28 职场文书
煤矿安全承诺书
2014/05/22 职场文书
2014年学习委员工作总结
2014/11/14 职场文书
2014年单位工作总结范文
2014/11/27 职场文书
幼儿学前班评语
2014/12/29 职场文书
2016大一新生军训心得体会
2016/01/11 职场文书