vue-router 源码实现前端路由的两种方式


Posted in Javascript onJuly 02, 2018

在学习 vue-router 的代码之前,先来简单了解一下前端路由。

前端路由主要有两种实现方法:

  • Hash 路由
  • History 路由

先来看看这两种方法的实现原理。

接着我们将用它们来简单实现一个自己的前端路由。

前端路由

Hash 路由

url 的 hash 是以 # 开头,原本是用来作为锚点,从而定位到页面的特定区域。当 hash 改变时,页面不会因此刷新,浏览器也不会向服务器发送请求。

http://www.xxx.com/#/home

同时, hash 改变时,并会触发相应的 hashchange 事件。所以,hash 很适合被用来做前端路由。当 hash 路由发生了跳转,便会触发 hashchange 回调,回调里可以实现页面更新的操作,从而达到跳转页面的效果。

window.addEventListener('hashchange', function () {
 console.log('render');
});

History 路由

HTML5 规范中提供了 history.pushStatehistory.replaceState 来进行路由控制。通过这两个方法,可以实现改变 url 且不向服务器发送请求。同时不会像 hash 有一个 # ,更加的美观。但是 History 路由需要服务器的支持,并且需将所有的路由重定向到根页面。

History 路由的改变不会去触发某个事件,所以我们需要去考虑如何触发路由更新后的回调。

有以下两种方式会改变 url:

  • 调用 history.pushState 或 history.replaceState;
  • 点击浏览器的前进与后退。

第一个方式可以封装一个方法,在调用 pushState(replaceState)后再调用回调。

function push (url) {
 window.history.pushState({}, null, url);
 handleHref();
}

function handleHref () {
 console.log('render');
}

第二个方式,浏览器的前进与后退会触发 popstate 事件。

window.addEventListener('popstate', handleHref);

路由实现

我们通过 <a> 标签来进行切换路由,通过一个 <div> 标签来装载各路由对应的页面内容。

参考 vue-router 的调用,我们会这么地调用一个 Router ,将路由与对应组件作为参数传入:

const router = new Router([
 {
  path: '/',
  component: 'home'
 },
 {
  path: '/book',
  component: 'book'
 },
 {
  path: '/movie',
  component: 'movie'
 }
]);

数组里是各路由对应的要显示的内容,接下来就来开始实现这个 Router

Hash 路由实现

Hash 路由 <a> 标签都需要带上 #

<div>
 <a href="#/" rel="external nofollow" >home</a>
 <a href="#/book" rel="external nofollow" >book</a>
 <a href="#/movie" rel="external nofollow" >movie</a>
  
 <div id="content"></div>
</div>

Router 的代码实现如下:

class Router {
 constructor (options) {
  this.routes = {};
  
  this.init();
  
  // 遍历,绑定视图更新
  options.forEach(item => {
   this.route(item.path, () => {
   	document.getElementById('content').innerHTML = item.component;
   });
  });
 }
 
 // 绑定监听事件
 init () {
  window.addEventListener('load', this.updateView.bind(this), false);
  window.addEventListener('hashchange', this.updateView.bind(this), false);
 }
 
 // 更新试图
 updateView () {
  const currentUrl = window.location.hash.slice(1) || '/';
  this.routes[currentUrl] && this.routes[currentUrl]();
 }
 
 // 将路由与回调函数关联
 route (path, cb) {
  this.routes[path] = cb;
 }
}

实现效果如下:

vue-router 源码实现前端路由的两种方式 

History 路由实现

History 路由需要服务器的支持,可以点击这里 的代码参考。

<div>
 <a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" data-href="/" rel="external nofollow" >home</a>
 <a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" data-href="/book" rel="external nofollow" >book</a>
 <a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" data-href="/movie" rel="external nofollow" >movie</a>
  
 <div id="content"></div>
</div>

Router 的代码实现如下:

class Router {
 constructor (options) {
  this.routes = {};

  this.init();
  this.bindEvent();

  // 遍历,绑定视图更新
  options.forEach(item => {
   this.route(item.path, () => {
    document.getElementById('content').innerHTML = item.component;
   });
  });
 }

 // 绑定点击事件
 bindEvent () {
  const _this = this;
  const links = document.getElementsByTagName('a');

  [].forEach.call(links, link => {
   link.addEventListener('click', function () {
    const url = this.getAttribute('data-href');
    _this.push(url);
   });
  });
 }

 // 绑定监听事件
 init () {
  window.addEventListener('load', this.updateView.bind(this), false);
  window.addEventListener('popstate', this.updateView.bind(this), false);
 }

 push (url) {
  window.history.pushState({}, null, url);
  this.updateView();
 }

 // 更新试图
 updateView () {
  const currentUrl = window.location.pathname || '/';
  this.routes[currentUrl] && this.routes[currentUrl]();
 }

 // 将路由与回调函数关联
 route (path, cb) {
  this.routes[path] = cb;
 }
}

实现效果如下:

vue-router 源码实现前端路由的两种方式

最后

前端路由实现方式有两种,分别是:

  1. Hash 路由
  2. History 路由

原理都是修改 url 的同时不刷新页面,不向服务器发送请求,通过监听特殊的事件来更新页面。

以上实现全部源码参考这里。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
html组件不可输入(只读)同时任何组件都有效
Apr 01 Javascript
ajax请求get与post的区别总结
Nov 04 Javascript
jquery使用正则表达式验证email地址的方法
Jan 22 Javascript
JavaScript实现自动消除按钮功能的方法
Aug 05 Javascript
基于javascript实现图片切换效果
Apr 17 Javascript
js实现纯前端的图片预览
Apr 27 Javascript
关于bootstrap日期转化,bootstrap-editable的简单使用,bootstrap-fileinput的使用详解
May 12 Javascript
关于JavaScript中的this指向问题总结篇
Jul 23 Javascript
自己动手封装一个React Native多级联动
Sep 19 Javascript
基于游标的分页接口实现代码示例
Nov 12 Javascript
基于js Canvas实现二次贝塞尔曲线
Dec 25 Javascript
JavaScript数组去重的几种方法
Apr 07 Javascript
React Native基础入门之调试React Native应用的一小步
Jul 02 #Javascript
vue-router 源码之实现一个简单的 vue-router
Jul 02 #Javascript
JavaScript设计模式之单例模式简单实例教程
Jul 02 #Javascript
JavaScript设计模式之建造者模式实例教程
Jul 02 #Javascript
JS实现的JSON序列化操作简单示例
Jul 02 #Javascript
JS内部事件机制之单线程原理
Jul 02 #Javascript
JS将网址url转化为JSON格式的方法
Jul 02 #Javascript
You might like
Netflix将与CLAMP、乙一以及冲方丁等6名知名制作人合伙展开原创动画计划!
2020/03/06 日漫
PHP5中的this,self和parent关键字详解教程
2007/03/19 PHP
Mootools 1.2教程 Fx.Tween的使用
2009/09/15 Javascript
比较全面的event对像在IE与FF中的区别 推荐
2009/09/21 Javascript
不提示直接关闭网页窗口的JS示例代码
2013/12/17 Javascript
javascript中定义私有方法说明(private method)
2014/01/27 Javascript
js中哈希表的几种用法总结
2014/01/28 Javascript
谷歌浏览器调试JavaScript小技巧
2014/12/29 Javascript
JavaScript中使用Object.prototype.toString判断是否为数组
2015/04/01 Javascript
JS返回iframe中frameBorder属性值的方法
2015/04/01 Javascript
JavaScript中var关键字的使用详解
2015/08/14 Javascript
javascript 常用验证函数总结
2016/06/28 Javascript
纯前端JavaScript实现Excel IO案例分享
2016/08/26 Javascript
jquery实现图片列表鼠标移入微动
2016/12/01 Javascript
详解vue嵌套路由-params传递参数
2017/05/23 Javascript
Vue组件化通讯的实例代码
2017/06/23 Javascript
MUI实现上拉加载和下拉刷新效果
2017/06/30 Javascript
微信小程序富文本渲染引擎的详解
2017/09/30 Javascript
原生JavaScript实现的简单放大镜效果示例
2018/02/07 Javascript
基于IView中on-change属性的使用详解
2018/03/15 Javascript
详解Angular中通过$location获取地址栏的参数
2018/08/02 Javascript
详解VUE单页应用骨架屏方案
2019/01/17 Javascript
js 解析 JSON 数据简单示例
2020/04/21 Javascript
[42:32]DOTA2上海特级锦标赛B组资格赛#2 Fnatic VS Spirit第二局
2016/02/27 DOTA
Python实现的tcp端口检测操作示例
2018/07/24 Python
python仿evething的文件搜索器实例代码
2019/05/13 Python
Python3常用内置方法代码实例
2019/11/18 Python
基于Python执行dos命令并获取输出的结果
2019/12/30 Python
TensorFlow tf.nn.conv2d实现卷积的方式
2020/01/03 Python
将pycharm配置为matlab或者spyder的用法说明
2020/06/08 Python
世界上最大的曲棍球商店:Pro Hockey Life
2017/10/30 全球购物
Python的两道面试题
2013/06/29 面试题
营销与策划专业求职信
2014/06/20 职场文书
建设办主任四风问题整改思路和措施
2014/09/20 职场文书
2015年学校信息技术工作总结
2015/05/25 职场文书
爱心捐款倡议书:点燃希望,传递温暖
2019/11/04 职场文书