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

news/2024/7/24 7:37:37 标签: vue-router

在学习 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.pushState 和 history.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);

路由实现

我们通过 标签来进行切换路由,通过一个

标签来装载各路由对应的页面内容。

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

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

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

Hash 路由实现

Hash 路由 标签都需要带上 # :

 <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;
 }
}

实现效果如下:

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;
 }
}

实现效果如下:

最后

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

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

http://www.niftyadmin.cn/n/1301155.html

相关文章

C语言银行系统

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<conio.h>#include<process.h> typedef struct a { char num[30]; //帐号 char name[30];//姓名 char password[30];//密码 float money;//金额} user;//用户结构体 typedef …

JavaScript中Promise函数then的奥秘探究

Promise概述 Promise对象是CommonJS工作组提出的一种规范&#xff0c;目的是为异步操作提供统一接口。 那么&#xff0c;什么是Promises&#xff1f; 首先&#xff0c;它是一个对象&#xff0c;也就是说与其他JavaScript对象的用法&#xff0c;没有什么两样&#xff1b;其次…

APK重签名以及可能遇到的问题

在网上的文章里&#xff0c;说的都是&#xff1a; 1.把apk包解压出来 2.删除META-INF文件夹 3.打包成zip 4.把zip后缀修改成apk后缀 5.用jarsigner打apk包重签名 但是&#xff0c;这里的步骤我们应该把1步骤、3步骤、4步骤删除。 删除1步骤、3步骤、4步骤的好处有两个&a…

C语言运算测试系统

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<conio.h> struct question{char level;//级别char qu[40];//问题char an[20];//答案}; struct qnode//链表结构体{struct question q;struct qnode *next;//指向下一个链表结构体…

VUE 全局变量的几种实现方式

1、全局变量专用模块 意思是说&#xff0c;用一个模块&#xff08;js or vue&#xff09;管理这套全局变量&#xff0c;模块里的变量用export &#xff08;最好导出的格式为对象&#xff0c;方便在其他地方调用&#xff09;暴露出去&#xff0c;当其它地方需要使用时&#xff0…

TiledMap简单使用

文章转载自&#xff1a;https://blog.csdn.net/Super_Cola/article/details/81084506 官网下载地址 http://www.mapeditor.org/ 官方文档 http://doc.mapeditor.org/ 参考的文章 http://blog.csdn.net/zhy_cheng/article/details/8308609 http://blog.csdn.net/zhy_che…

Promise.all中对于reject的处理方法

写了个小爬虫&#xff0c;用axios.all同时请求多个页面时&#xff0c;国内网络的原因很容易就超时然后reject了&#xff0c;佛系resolve不可取啊&#xff0c;然后想到可以实现一个“重发失败请求”的功能。 Promise.all(requestPromises).then(…).catch(…) 会在所有requestP…

软件设计师完美通过

前些日子回家玩了一个月&#xff0c;家里没网。 今天刚刚到学校&#xff0c;怀着坦克的心情查询11月13的软件设计师的考试成绩&#xff0c; 59&#xff0c;57 心里还是长长的舒了一口气。个人觉得&#xff0c;软件考试不只是考你的编程能力&#xff0c;它可能更注重软件综合能…