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 相关文章推荐
JavaScript实现x秒后自动跳转到一个页面
Jan 03 Javascript
jQuery的deferred对象使用详解
Sep 25 Javascript
微信小程序 生命周期和页面的生命周期详细介绍
Jan 19 Javascript
for循环 + setTimeout 结合一些示例(前端面试题)
Aug 30 Javascript
微信小程序switch组件使用详解
Jan 31 Javascript
AngularJS中ng-options实现下拉列表的数据绑定方法
Aug 13 Javascript
vue中的router-view组件的使用教程
Oct 23 Javascript
jQuery实现网页拼图游戏
Apr 22 jQuery
微信小程序template模板与component组件的区别和使用详解
May 22 Javascript
微信小程序获取地理位置及经纬度授权代码实例
Sep 18 Javascript
vue封装可复用组件confirm,并绑定在vue原型上的示例
Oct 31 Javascript
解决vue安装less报错Failed to compile with 1 errors的问题
Oct 22 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版
2012/04/20 PHP
php判断电脑访问、手机访问的例子
2014/05/10 PHP
php json转换成数组形式代码分享
2014/11/10 PHP
phpmailer简单发送邮件的方法(附phpmailer源码下载)
2016/06/13 PHP
Laravel5.7 Eloquent ORM快速入门详解
2019/04/12 PHP
javascript同步Import,同步调用外部js的方法
2008/07/08 Javascript
基于jquery的返回顶部效果(兼容IE6)
2011/01/17 Javascript
JQuery获取当前屏幕的高度宽度的实现代码
2011/07/12 Javascript
jQueryUI写一个调整分类的拖放效果实现代码
2012/05/10 Javascript
Javascript表格翻页效果实现思路及代码
2013/08/23 Javascript
jquery easyui中treegrid用法的简单实例
2014/02/18 Javascript
在myeclipse中如何加入jquery代码提示功能
2014/06/03 Javascript
Javascript学习指南
2014/12/01 Javascript
jquery使整个div区域可以点击的方法
2015/06/24 Javascript
浅谈javascript的Touch事件
2015/09/27 Javascript
jquery实现全屏滚动
2015/12/28 Javascript
微信小程序 限制1M的瘦身技巧与方法详解
2017/01/06 Javascript
Vue源码探究之状态初始化
2018/11/14 Javascript
Vue对象赋值视图不更新问题及解决方法
2019/06/03 Javascript
Vue父组件如何获取子组件中的变量
2019/07/24 Javascript
使用layui监听器监听select下拉框,事件绑定不成功的解决方法
2019/09/28 Javascript
python下载文件时显示下载进度的方法
2015/04/02 Python
利用Python脚本在Nginx和uwsgi上部署MoinMoin的教程
2015/05/05 Python
Python编程之gui程序实现简单文件浏览器代码
2017/12/08 Python
Python使用Scrapy爬虫框架全站爬取图片并保存本地的实现代码
2018/03/04 Python
Python制作exe文件简单流程
2019/01/24 Python
Django实现学生管理系统
2019/02/26 Python
关于Python3的import问题(pycharm可以运行命令行import错误)
2020/11/18 Python
Python接口自动化系列之unittest结合ddt的使用教程详解
2021/02/23 Python
全球知名鞋履品牌授权零售商:Journeys
2016/09/17 全球购物
Lacoste美国官网:经典POLO衫品牌
2016/10/12 全球购物
新西兰珠宝品牌:Michael Hill
2017/09/16 全球购物
代码中finally中的代码会不会执行
2012/02/06 面试题
婚礼答谢词
2015/01/04 职场文书
Pytorch中Softmax和LogSoftmax的使用详解
2021/06/05 Python
微信小程序APP页面的之间的相互传递参数以及自定义组件
2022/04/19 Javascript