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 相关文章推荐
jQuery 使用手册(六)
Sep 23 Javascript
js 上传图片预览问题
Dec 06 Javascript
jquery中动态效果小结
Dec 16 Javascript
jQuery代码优化 选择符篇
Nov 01 Javascript
在javascript中执行任意html代码的方法示例解读
Dec 25 Javascript
JavaScript检查某个function是否是原生代码的方法
Aug 20 Javascript
巧用weui.topTips验证数据的实例
Apr 17 Javascript
微信小程序使用progress组件实现显示进度功能【附源码下载】
Dec 12 Javascript
VUE+Element环境搭建与安装的方法步骤
Jan 24 Javascript
Javascript和jquery在selenium的使用过程
Oct 31 jQuery
基于vue实现图片验证码倒计时60s功能
Dec 10 Javascript
html中两种获取标签内的值的方法
Jun 16 jQuery
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 clearstatcache()函数详解
2010/03/02 PHP
PHP实现阳历到农历转换的类实例
2015/03/07 PHP
php对文件进行hash运算的方法
2015/04/03 PHP
使用PHP如何实现高效安全的ftp服务器(一)
2015/12/20 PHP
PHP使用token防止表单重复提交的方法
2016/04/07 PHP
xmlHTTP实例
2006/10/24 Javascript
js取滚动条的尺寸的函数代码
2011/11/30 Javascript
jquery.bgiframe.js在IE9下提示INVALID_CHARACTER_ERR错误
2013/01/11 Javascript
javascript ajax 仿百度分页函数
2013/10/29 Javascript
javascript中动态加载js文件多种解决办法总结
2013/11/15 Javascript
纯JS实现根据CSS的class选择DOM
2014/03/22 Javascript
jquery队列函数用法实例
2014/12/16 Javascript
微信小程序 label 组件详解及简单实例
2017/01/10 Javascript
js编写简单的计时器功能
2017/07/15 Javascript
phantomjs导出html到pdf的方法总结
2017/10/19 Javascript
webpack配置之后端渲染详解
2017/10/26 Javascript
node.js文件上传重命名以及移动位置的示例代码
2018/01/19 Javascript
基于vue 实现表单中password输入的显示与隐藏功能
2019/07/19 Javascript
JSON 入门教程基础篇 json入门学习笔记
2020/09/22 Javascript
一起来了解一下JavaScript的预编译(小结)
2021/03/01 Javascript
[01:00:30]完美世界DOTA2联赛循环赛 Inki vs Matador BO2第二场 10.31
2020/11/02 DOTA
python实现从字符串中找出字符1的位置以及个数的方法
2014/08/25 Python
深入解析Python中的urllib2模块
2015/11/13 Python
关于python多重赋值的小问题
2019/04/17 Python
人工神经网络算法知识点总结
2019/06/11 Python
自适应线性神经网络Adaline的python实现详解
2019/09/30 Python
Spartoo葡萄牙鞋类网站:线上销售鞋履与时尚配饰
2017/01/11 全球购物
黄色火烈鸟:De Gele Flamingo
2019/03/18 全球购物
Nike墨西哥官网:Nike MX
2020/08/30 全球购物
写给女生的道歉信
2014/01/14 职场文书
给妈妈洗脚活动方案
2014/08/16 职场文书
广告设计专业毕业生自我鉴定
2014/09/27 职场文书
2015年银行柜员工作总结报告
2015/04/01 职场文书
公司文体活动总结
2015/05/07 职场文书
详解盒子端CSS动画性能提升
2021/05/24 HTML / CSS
基于redis+lua进行限流的方法
2022/07/23 Redis