前端路由原理

news/2024/7/19 14:04:37 标签: js

前言

前端三大框架 AngularReactVue ,它们的路由解决方案 angular/routerreact-routervue-router 都是基于前端路由原理进行封装实现的,因此将前端路由原理进行了解和掌握是很有必要的,因为我们再使用的过程中也难免会遇到一些坑,一旦我们掌握了它的实现原理,那么就能在开发中对路由的使用更加游刃有余。

一、什么是路由?

​ 路由的概念起源于服务端,在以前前后端不分离的时候,由后端来控制路由,当接收到客户端发来的 HTTP 请求,就会根据所请求的相应 URL,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。对于最简单的静态资源服务器,可以认为,所有 URL 的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据的处理等等。然后根据这些读取的数据,在服务器端就使用相应的模板来对页面进行渲染后,再返回渲染完毕的页面。它的好处与缺点非常明显:

  • 好处:安全性好,SEO 好;
  • 缺点:加大服务器的压力,不利于用户体验,代码冗合不好维护;

​ 也正是由于后端路由还存在着自己的不足,前端路由才有了自己的发展空间。对于前端路由来说,路由的映射函数通常是进行一些 DOM 的显示和隐藏操作。这样,当访问不同的路径的时候,会显示不同的页面组件。前端路由主要有以下两种实现方案:

  • Hash
  • History

当然,前端路由也存在缺陷:使用浏览器的前进,后退键时会重新发送请求,来获取数据,没有合理地利用缓存。但总的来说,现在前端路由已经是实现路由的主要方式了,前端三大框架 AngularReactVue ,它们的路由解决方案 angular/routerreact-routervue-router 都是基于前端路由进行开发的,因此将前端路由进行了解和 掌握是很有必要的,下面我们分别对两种常见的前端路由模式 HashHistory 进行讲解。

二、前端路由的两种实现

2.1、Hash 模式

2.1.1、原理

早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理也很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 '#search'

https://www.word.com#search

此外,hash 也存在下面几个特性:

  • URLhash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送。
  • hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换。
  • 我们可以使用 hashchange 事件来监听 hash 的变化。

我们可以通过两种方式触发 hash 变化,一种是通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 就会发生改变,也就会触发 hashchange 事件了:

<a href="#search">search</a>

还有一种方式就是直接使用 JavaScript来对 loaction.hash 进行赋值,从而改变 URL,触发 hashchange 事件:

location.hash="#search"

以下实现我们采用第2种方式来实现。

2.1.2、实现

我们先定义一个父类 BaseRouter,用于实现 Hash 路由和 History 路由的一些共有方法;

export class BaseRouter {
  // list 表示路由表
  constructor(list) {
    this.list = list;
  }
  // 页面渲染函数
  render(state) {
    let ele = this.list.find(ele => ele.path === state);
    ele = ele ? ele : this.list.find(ele => ele.path === '*');
    ELEMENT.innerText = ele.component;
  }
}

我们简单实现了 push 压入功能、go 前进/后退功能,相关代码的注释都已经标上,简单易懂,就不在一 一介绍,参见如下:

export class HashRouter extends BaseRouter {
  constructor(list) {
    super(list);
    this.handler();
    // 监听 hashchange 事件
    window.addEventListener('hashchange', e => {
      this.handler();
    });
  }
  // hash 改变时,重新渲染页面
  handler() {
    this.render(this.getState());
  }
  // 获取 hash 值
  getState() {
    const hash = window.location.hash;
    return hash ? hash.slice(1) : '/';
  }
  // push 新的页面
  push(path) {
    window.location.hash = path;
  }
  // 获取 默认页 url
  getUrl(path) {
    const href = window.location.href;
    const i = href.indexOf('#');
    const base = i >= 0 ? href.slice(0, i) : href;
    return base +'#'+ path;
  }
  // 替换页面
  replace(path) {
    window.location.replace(this.getUrl(path));
  }
  // 前进 or 后退浏览历史
  go(n) {
    window.history.go(n);
  }
}

2.1.3、效果图

Hash 模式的路由实现例子的效果图如下所示:

1.png

2.2、History 模式

2.2.1、原理

前面的 hash 虽然也很不错,但使用时都需要加上 #,并不是很美观。因此到了 HTML5,又提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState()history.repalceState()。这两个 API可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:

window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);

此外,history 存在下面几个特性:

  • pushStaterepalceState 的标题(title):一般浏览器会忽略,最好传入 null
  • 我们可以使用 popstate  事件来监听 url 的变化;
  • history.pushState()history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面渲染;

2.2.2、实现

我们同样简单实现了 push 压入功能、go 前进/后退功能,相关代码的注释都已经标上,简单易懂,就不在一 一介绍,参见如下:

export class HistoryRouter extends BaseRouter {
  constructor(list) {
    super(list);
    this.handler();
    // 监听 popstate 事件
    window.addEventListener('popstate', e => {
      console.log('触发 popstate。。。');
      this.handler();
    });
  }
  // 渲染页面
  handler() {
    this.render(this.getState());
  }
  // 获取 url 
  getState() {
    const path = window.location.pathname;
    return path ? path : '/';
  }
  // push 页面
  push(path) {
    history.pushState(null, null, path);
    this.handler();
  }
  // replace 页面
  replace(path) {
    history.replaceState(null, null, path);
    this.handler();
  }
   // 前进 or 后退浏览历史
  go(n) {
    window.history.go(n);
  }
}

2.2.3、效果图

History 模式的路由实现例子的效果图如下所示:

1.png

2.3、两种路由模式的对比

对比点Hash 模式History 模式
美观性带着 # 字符,较丑简洁美观
兼容性>= ie 8,其它主流浏览器>= ie 10,其它主流浏览器
实用性不需要对服务端做改动需要服务端对路由进行相应配合设置

三、总结

本文我们大致介绍了什么路由、前端路由的源起、以及分析了两种前端路由:Hash 模式和 History 模式的原理以及简单功能实现,文中例子的代码实现已经放到 github 上面:github.com/fengshi123/… 。通过本文对前端路由原理的掌握,这时你就可以基础原理基础去阅读 vue-routerreact-router 的源码实现了。

github地址为:github.com/fengshi123/… ,上面汇总了作者所有的博客文章,如果喜欢或者有所启发,请帮忙给个 star ~,对作者也是一种鼓励。

参考文献

1、浅谈前端路由:www.jianshu.com/p/d2aa8fb95…

2、前端路由:segmentfault.com/a/119000001…

原文链接:https://juejin.cn/post/6844903906024095751


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

相关文章

CSDN博客积分规则和获取积分方法

原创连接&#xff1a; http://blog.csdn.net/xi_hong_shi/article/details/7742963 博客积分是衡量博客水平的重要标准&#xff0c;博客的排名也将按照积分排列。积分规则具体如下&#xff1a; 1、每发布一篇原创或者翻译文章&#xff1a;可获得10分 2、每发布一篇转载文章…

typescript 中的keyof、 in

原文链接&#xff1a;https://juejin.cn/post/6844904145732763655#heading-0 keyof 定义 keyof与Object.keys略有相似&#xff0c;只是 keyof 是取 interface 的键&#xff0c;而且 keyof 取到键后会保存为联合类型。 interface iUserInfo {name: string;age: number; } …

那些事情(读后感)

1,事业永远第一 虽然金钱不是万能的,但没有钱是万万不能的,虽然这句话很俗,但绝对有道理,所以30岁之前,请把你大部分精力放在你的事业上。 PS&#xff1a;现实的社会和物价的飞涨&#xff08;现在青菜都4块以上&#xff0c;深圳&#xff09;&#xff0c;没有稳定的工作和收…

TypeScript中的泛型工具

原文链接&#xff1a;https://juejin.cn/post/6844904147167215624#heading-1 本期涉及的操作符如下&#xff1a; PartialRequiredReadonlyPick<T,K extends keyof T>Record<K extends keyof any, T>Exclude<T,U>Extract<T,U>Omit<T, K extends …

俞敏洪+马云+牛根生+史玉柱经典语录

&#xff11;&#xff0e;女人如果因为觉得一个男生帅就跟嫁给他&#xff0c;这是好色&#xff1b;男生因为女生漂亮而娶她&#xff0c;是审美。&#xff12;.为什么你不要自傲和自卑&#xff1f;你可以说自己是最好的&#xff0c;但不能说自己是全校最好的、全北京最好的、全国…

Cesium orientation 和 设置初始角度

一. Cesium orientation orientation-相机镜头对准的方法.heading-代表镜头左右方向,正值为右,负值为左,360度和0度是一样的 pitch-代表镜头上下方向,正值为上,负值为下. roll-代表镜头左右倾斜.正值,向右倾斜,负值向左倾斜 二. 设置初始化角度 1.先用鼠标手动调整出你想要的角…

假如生活欺骗了你

最近某好友的公司&#xff0c;到了一年一度的调职调薪时段。 作为一个独立的中立的第三方&#xff0c;我听该好友详细讲述了他们公司的一些情况&#xff0c;颇有感触。 调职调薪的结果&#xff0c;比较杯具。 就公司内部的 职称/头衔/Title而言&#xff0c;大多数人都有提升1级…

Cesium 中的pick

在cesium中&#xff0c;想获取不同的对象&#xff0c;需要通过pick方法来进行拾取&#xff0c;但是Cesium中有多种pick的方法&#xff0c;例如 scene中有pick、pickPosition、及drillPick等&#xff0c;camera中有getPickRay、pickEllipsoid等&#xff0c;globel中有pick&#…