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.js 的时候应该特别注意的几个问题.
Apr 12 Javascript
Javascript图像处理—为矩阵添加常用方法
Dec 27 Javascript
跟我学习javascript的浮点数精度
Nov 16 Javascript
全面理解闭包机制
Jul 11 Javascript
微信小程序 progress组件详解及实例代码
Oct 25 Javascript
探讨AngularJs中ui.route的简单应用
Nov 16 Javascript
常用的javascript设计模式
Jan 11 Javascript
react native仿微信PopupWindow效果的实例代码
Aug 07 Javascript
Vue程序调试的方法
Jun 17 Javascript
Vue+Typescript中在Vue上挂载axios使用时报错问题
Aug 07 Javascript
node.js使用stream模块实现自定义流示例
Feb 13 Javascript
详细分析Node.js 模块系统
Jun 28 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
修改Zend引擎实现PHP源码加密的原理及实践
2008/04/14 PHP
php die()与exit()的区别实例详解
2016/12/03 PHP
Yii框架使用PHPExcel导出Excel文件的方法分析【改进版】
2019/07/24 PHP
tp5 实现列表数据根据状态排序
2019/10/18 PHP
javascript实现 在光标处插入指定内容
2007/05/25 Javascript
动态加载图片路径 保持JavaScript控件的相对独立性
2010/09/06 Javascript
JavaScript Promise启示录
2014/08/12 Javascript
javascript trim函数在IE下不能用的解决方法
2014/09/12 Javascript
angularjs指令中的compile与link函数详解
2014/12/06 Javascript
在HTML中插入JavaScript代码的示例
2015/06/03 Javascript
深入浅析JavaScript中prototype和proto的关系
2015/11/15 Javascript
防止重复发送 Ajax 请求
2017/02/15 Javascript
web.js.字符串与正则表达式操作
2017/05/13 Javascript
vue按需加载组件webpack require.ensure的方法
2017/12/13 Javascript
深入浅析Vue全局组件与局部组件的区别
2018/06/15 Javascript
Vue实现简单分页器
2018/12/29 Javascript
9102年webpack4搭建vue项目的方法步骤
2019/02/20 Javascript
Node.js + express基本用法教程
2019/03/14 Javascript
vue-socket.io跨域问题有效解决方法
2020/02/11 Javascript
js制作提示框插件
2020/12/24 Javascript
[06:07]DOTA2-DPC中国联赛 正赛 Ehome vs VG 选手采访
2021/03/11 DOTA
Python3.6使用tesseract-ocr的正确方法
2018/10/17 Python
python实现桌面托盘气泡提示
2019/07/29 Python
Django自带日志 settings.py文件配置方法
2019/08/30 Python
Python基于smtplib模块发送邮件代码实例
2020/05/29 Python
对pytorch中x = x.view(x.size(0), -1) 的理解说明
2021/03/03 Python
广州喜创信息技术有限公司JAVA软件工程师笔试题
2012/10/17 面试题
亲戚结婚的请假条
2014/02/11 职场文书
纠纷协议书
2014/04/16 职场文书
质量在我心中演讲稿
2014/09/02 职场文书
电子银行业务授权委托书
2014/10/10 职场文书
2014年学前班工作总结
2014/12/08 职场文书
居安思危观后感
2015/06/11 职场文书
中秋联欢会主持词
2015/07/04 职场文书
22句经典语录:送给优柔寡断和胡思乱想的朋友们
2019/12/13 职场文书
HTML页面滚动时部分内容位置固定不滚动的实现
2021/04/14 HTML / CSS