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 相关文章推荐
Prototype RegExp对象 学习
Jul 19 Javascript
Web 前端设计模式--Dom重构 提高显示性能
Oct 22 Javascript
JS和Jquery获取和修改label的值的示例代码
Jan 15 Javascript
jQuery使用之设置元素样式用法实例
Jan 19 Javascript
Javascript实现图片轮播效果(一)让图片跳动起来
Feb 17 Javascript
漫谈JS引擎的运行机制 你应该知道什么
Jun 15 Javascript
jQuery实现获取隐藏div高度的方法示例
Feb 09 Javascript
Vue组件之Tooltip的示例代码
Oct 18 Javascript
webpack 插件html-webpack-plugin的具体使用
Apr 09 Javascript
JavaScript引用类型Function实例详解
Aug 09 Javascript
解决vue props 拿不到值的问题
Sep 11 Javascript
vue 微信扫码登录(自定义样式)
Jan 06 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实现将GB编码转换为UTF8
2006/11/25 PHP
thinkphp5框架扩展redis类方法示例
2019/05/06 PHP
传递参数的标准方法(jQuery.ajax)
2008/11/19 Javascript
jquery下实现overlay遮罩层代码
2010/08/25 Javascript
关于IE BUG与字符串截取substr的解决办法
2013/04/10 Javascript
Jquery ajaxStart()与ajaxStop()方法(实例讲解)
2013/12/18 Javascript
javascript中数组和字符串的方法对比
2016/07/20 Javascript
JS 事件绑定、事件监听、事件委托详细介绍
2016/09/28 Javascript
JavaScript中定时控制Throttle、Debounce和Immediate详解
2016/11/17 Javascript
vue如何引用其他组件(css和js)
2017/04/13 Javascript
JS使用tofixed与round处理数据四舍五入的区别
2017/10/25 Javascript
JS 中可以提升幸福度的小技巧(可以识别更多另类写法)
2018/07/28 Javascript
详解webpack-dev-server使用方法
2018/09/14 Javascript
Layui给switch添加响应事件的例子
2019/09/03 Javascript
layui 关闭open弹出框 刷新table表格页面的方法
2019/09/16 Javascript
Python 流程控制实例代码
2009/09/25 Python
Python学习笔记(二)基础语法
2014/06/06 Python
python清除字符串里非数字字符的方法
2015/07/02 Python
Python中list列表的一些进阶使用方法介绍
2015/08/15 Python
Python 登录网站详解及实例
2017/04/11 Python
Python统计单词出现的次数
2018/04/04 Python
对Xpath 获取子标签下所有文本的方法详解
2019/01/02 Python
python3.6+django2.0+mysql搭建网站过程详解
2019/07/24 Python
Python 、Pycharm、Anaconda三者的区别与联系、安装过程及注意事项
2019/10/11 Python
python sorted函数原理解析及练习
2020/02/10 Python
Python接口测试结果集实现封装比较
2020/05/01 Python
利用css3画个同心圆示例代码
2017/07/03 HTML / CSS
五四青年节演讲稿
2014/05/26 职场文书
职务任命书范本
2014/06/05 职场文书
学生检讨书怎么写?
2014/10/10 职场文书
2014党的群众路线教育实践活动总结报告
2014/10/31 职场文书
2015年大学生实习评语
2015/03/25 职场文书
联谊会开场白
2015/06/01 职场文书
运动会新闻报道稿
2015/07/22 职场文书
运动会跳远广播稿
2015/08/19 职场文书
Java实现多文件上传功能
2021/06/30 Java/Android