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 validate 中文API 附validate.js中文api手册
Jul 31 Javascript
无限树Jquery插件zTree的常用功能特性总结
Sep 11 Javascript
什么是Vue.js框架 为什么选择它?
Oct 17 Javascript
详解Vue 中 extend 、component 、mixins 、extends 的区别
Dec 20 Javascript
element-ui 限制日期选择的方法(datepicker)
May 16 Javascript
vue使用技巧及vue项目中遇到的问题
Jun 04 Javascript
关于AngularJS中ng-repeat不更新视图的解决方法
Sep 30 Javascript
js实现鼠标拖拽缩放div实例代码
Mar 25 Javascript
微信小程序之 catalog 切换实现解析
Sep 12 Javascript
layui表格数据复选框回显设置方法
Sep 13 Javascript
vue实现将数据存入vuex中以及从vuex中取出数据
Nov 08 Javascript
Javascript类型判断相关例题及解析
Aug 26 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
windows下PHP_intl.dll正确配置方法(apache2.2+php5.3.5)
2014/01/14 PHP
php中count获取多维数组长度的方法
2014/11/03 PHP
PHP通过API获取手机号码归属地
2015/05/28 PHP
讲解WordPress开发中一些常用的debug技巧
2015/12/18 PHP
详解js异步文件加载器
2016/01/24 PHP
浅析PHP开发规范
2018/02/05 PHP
thinkphp5使html5实现动态跳转的例子
2019/10/16 PHP
Javascript中字符串和数字的操作方法整理
2017/01/22 Javascript
javascript 判断一个对象为数组的方法
2017/05/03 Javascript
详解在Node.js中发起HTTP请求的5种方法
2019/01/10 Javascript
微信小程序如何使用globalData的方法
2019/06/06 Javascript
移动端手指操控左右滑动的菜单
2019/09/08 Javascript
Vue+iview+webpack ie浏览器兼容简单处理
2019/09/20 Javascript
Vue中component标签解决项目组件化操作
2020/09/04 Javascript
python之模拟鼠标键盘动作具体实现
2013/12/30 Python
python正则实现提取电话功能
2018/02/24 Python
基于DataFrame筛选数据与loc的用法详解
2018/05/18 Python
Python smtplib实现发送邮件功能
2018/05/22 Python
Python反射和内置方法重写操作详解
2018/08/27 Python
详解利用Python scipy.signal.filtfilt() 实现信号滤波
2019/06/05 Python
详解将Python程序(.py)转换为Windows可执行文件(.exe)
2019/07/19 Python
Django的用户模块与权限系统的示例代码
2019/07/24 Python
Python Collatz序列实现过程解析
2019/10/12 Python
Python结合Window计划任务监测邮件的示例代码
2020/08/05 Python
Python实现对word文档添加密码去除密码的示例代码
2020/12/29 Python
意大利在线药房:shop-farmacia.it
2019/03/12 全球购物
微软巴西官方网站:Microsoft Brasil
2019/09/26 全球购物
法国在线药房:DoctiPharma
2020/10/21 全球购物
递归计算如下递归函数的值(斐波拉契)
2012/02/04 面试题
简历自荐信
2013/12/02 职场文书
技术学校毕业生求职信分享
2013/12/02 职场文书
吸烟检讨书2000字
2014/02/13 职场文书
2014年医学生毕业自我鉴定
2014/03/26 职场文书
求职自我评价范文100字
2014/09/23 职场文书
2015年度个人业务工作总结
2015/04/27 职场文书
使用Django框架创建项目
2022/06/10 Python