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 相关文章推荐
判断客户端浏览器是否安装了Flash插件的多种方法
Aug 11 Javascript
仿当当网淘宝网等主流电子商务网站商品分类导航菜单
Sep 25 Javascript
Js操作树节点自动折叠展开的几种方法
May 05 Javascript
Vue 2中ref属性的使用方法及注意事项
Jun 12 Javascript
Three.js入门之hello world以及如何绘制线
Sep 25 Javascript
Vuex 进阶之模块化组织详解
Jan 12 Javascript
浅析从vue源码看观察者模式
Jan 29 Javascript
webpack+vuex+axios 跨域请求数据的示例代码
Mar 06 Javascript
jQuery实现文字超过1行、2行或规定的行数时自动加省略号的方法
Mar 28 jQuery
JS实现DOM删除节点操作示例
Apr 04 Javascript
Js中使用正则表达式验证输入是否有特殊字符
Sep 07 Javascript
最全vue的vue-amap使用高德地图插件画多边形范围的示例代码
Jul 17 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用SAX解析XML的实现代码与问题分析
2011/08/22 PHP
微信支付开发教程(一)微信支付URL配置
2014/05/28 PHP
ThinkPHP之R方法实例详解
2014/06/20 PHP
async和DOM Script文件加载比较
2014/07/20 PHP
PHP与服务器文件系统的简单交互
2016/10/21 PHP
php 删除一维数组中某一个值元素的操作方法
2018/02/01 PHP
PHP+MySQL实现输入页码跳转到指定页面功能示例
2018/06/01 PHP
laravel 中某一字段自增、自减的例子
2019/10/11 PHP
extjs fckeditor集成代码
2009/05/10 Javascript
js日期时间补零的小例子
2013/03/05 Javascript
Jquery图片延迟加载插件jquery.lazyload.js的使用方法
2014/05/21 Javascript
vue.js vue-router如何实现无效路由(404)的友好提示
2017/12/20 Javascript
微信小程序仿朋友圈发布动态功能
2018/07/15 Javascript
vue过滤器用法实例分析
2019/03/15 Javascript
浅谈Vue项目骨架屏注入实践
2019/08/05 Javascript
JavaScript实现多球运动效果
2020/09/07 Javascript
学习python (1)
2006/10/31 Python
python斐波那契数列的计算方法
2018/09/27 Python
详解Django+Uwsgi+Nginx 实现生产环境部署
2018/11/06 Python
python批量修改文件夹及其子文件夹下的文件内容
2019/03/15 Python
Python 通过打码平台实现验证码的实现
2019/05/13 Python
python使用turtle绘制国际象棋棋盘
2019/05/23 Python
使用Django和Postgres进行全文搜索的实例代码
2020/02/13 Python
python实现查找所有程序的安装信息
2020/02/18 Python
python脚本和网页有何区别
2020/07/02 Python
印度服装购物网站:Limeroad
2018/09/26 全球购物
Nisbets爱尔兰:英国最大的厨房和餐饮设备供应商
2019/01/26 全球购物
大学生饮食配送创业计划书
2014/01/04 职场文书
出纳工作岗位责任制
2014/02/02 职场文书
应届大专生自荐书
2014/06/16 职场文书
药店营业员岗位职责
2015/04/14 职场文书
乱世佳人观后感
2015/06/08 职场文书
幼儿园体操比赛口号
2015/12/25 职场文书
跟班学习心得体会(共6篇)
2016/01/23 职场文书
2016年党风廉政建设承诺书
2016/03/25 职场文书
幽默导游词应该怎么写?
2019/08/26 职场文书