🔥手撸JS系列:手写一个React 路由库(未完结)

398 阅读6分钟

一、路由

1.路由定义

在现代前端开发中,路由是非常重要的一环。但路由到底是什么呢?

  1. 路由就是指随着浏览器地址栏的变化,展示给用户的页面也不相同。
  2. 路由就是URL到函数的映射。这是从路由的实现原理上来解释路由是什么的。

2.路由的发展

单页应用并没有普及时,后端路由担任着比较大的作用。

后端路由又可称之为服务器端路由,因为对于服务器来说,当接收到客户端发来的HTTP请求,就会根据所请求的相应URL,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。对于最简单的静态资源服务器,可以认为,所有URL的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据的处理,等等。然后根据这些读取的数据,在服务器端就使用相应的模板来对页面进行渲染后,再返回渲染完毕的页面。

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

此篇文章重在前端理由实现,那么后端路由暂时就说这么多。对于前端路由来说,路由的映射函数通常是进行一些DOM的显示和隐藏操作。这样,当访问不同的路径的时候,会显示不同的页面组件。前端路由主要有以下两种实现方案:

  • hash
  • history API

二、前端路由实现原理

1.通过location.hash实现前端路由

hash就是一个url#后面的部分,也叫做url的锚部分,锚部分在服务器端会被自动忽略,但是在浏览器中是可以通过loaction.hash来获取的。通过hash方法实现前端路由主要是用到的是onhashchange事件,这个事件可以实时监听urlhash值的变化,由此来根据hash值的变化进行一些DOM的切换操作。

//html中
<element onhashchange="myScript">
//javascript中
object.onhashchange=function(){myScript};
//Javascript中,使用addEventListener()方法:
object.addEventListener("hashchange", myScript);

1. onhashchange事件的触发条件:

1.改变url地址,在最后面增加或改变其hash值。

2.改变location.hreflocation.hash的值。

3.点击带有锚点的链接。

4.浏览器前进后退可能导致hash的变化,前提是两个网页地址中的hash值不同。

2. 利用Hash实现前端路由:

当浏览器地址栏urlhash值发生变化时,就会触发onhashchange事件,这时通过window.location.hash可以拿到当前浏览器的urlhash值,注意此时的hash值是带有#的,因此需要对其值进行相应的处理,去掉#,并且如果当前url不含hash值的话,就将其当做根目录处理。之后将url和相应的组件函数进行映射,根据不同的hash值执行不同的回调函数,也就是加载相应的组件。

2.通过window.historypopstate实现前端路由

1.history对象介绍

浏览器窗口会为用户提供一个history对象,用来保存用户操作页面的历史,我们在浏览网页时的前进后退操作都是基于这个对象来实现的。在前端路由的实现过程中主要用到了history对象里的history.pushState()history.replaceState()这两个接口。

history.replaceState(dataObj,title,url);

history.pushState(dataObj,title,url);

pushState()replaceState()方法很类似,二者均接受三个参数,分别是state、title和url。

  1. state:用来存放将要插入的history实体的相关信息,它是一个json格式的参数。
  2. 传入history实体的标题,需要注意的是firefox现在会自动忽略掉这个参数。
  3. 用来传递新的history实体的相对路径,如果其值为null则表示当前要插入的history实体与前一个实体一致,没有改变。

2.replaceState()方法与pushState()方法的唯一区别

replaceState()方法会将最新一条的history实体覆盖掉,而不是直接添加,这种情况在处理例如登录页面这些不需要记录到history中的情况时非常有用。

这里需要注意的是history提供的这两个方法不会主动出发浏览器页面的刷新,只是history对象包括地址栏的内容会发生改变,当出发前进后退等history事件时才会进行相应的响应。另外,作为参数传入的url也会受到同源策略的限制,如果出现跨域等情况会导致报错。

3. popstate是官方提供的事件,当history中的记录发生改变时就会触发该事件。

4. 利用history实现前端路由:

当想要跳转到指定url的时候,将目标url通过pushState()或者replaceState()方法填入到history和地址栏中,此时由于上述两种方法不会主动进行页面刷新,因此页面仍停留在当前页面,只是url地址发生了改变。之后通过popstate事件响应,执行相应的回调函数,实现前端组件间的切换。

三、前端路由的优势

  1. 页面刷新速度快。由于后端路由在请求一个新路径时,会重新向服务器发送请求,之后再根据服务器的相应结果重新渲染页面,这个过程会受到网络延迟的影响,而前端路由省略了整个请求过程,只是完成部分组件间的切换,因此页面的刷新速度会相对较快,用户体验相对较好。
  1. 复用性强。使用前端路由,代码中的layout、css、js都可以共用,以此来减少重复加载,提供程序性能。
  1. 页面状态可记录。不使用前端路由,仅通过ajax进行页面局部切换的单页应用,由于url始终保持不变,因此页面的状态是无法记录的,而前端路由很好的解决了这个问题。

四、React-Router和React-Router-Dom路由实现

项目GIT地址

1.环境搭建

你可以使用create-react-app搭建,也可以自己搭建一个最简单的开发环境。在入口页面我们写上最简单的不掺和路由的方法。

import React from 'react';
import ReactDom from 'react-dom';

ReactDom.render(<div>ReactDom</div>,document.getElementById("root"));

浏览器效果为:

2.React-RouterReact-Router-Dom介绍

1.React-RouterReact-Router-Dom区别

通常我们会看到以下写法

import {Switch, Route, Router} from 'react-router';
import {HashHistory, Link} from 'react-router-dom';

还有另外一种写法

import {Swtich, Route, Router, HashHistory, Link} from 'react-router-dom';

react-router: 实现了路由的核心功能。

react-router-dom: 基于react-router,加入了在浏览器运行环境下的一些功能,例如:Link组件,会渲染一个a标签。BrowserRouterHashRouter组件,前者使用pushStatepopState事件构建路由,后者使用window.location.hashhashchange事件构建路由。

我们看下React-Router-Dom实现的Switch

// Written in this round about way for babel-transform-imports
import { Switch } from 'react-router'
export default Switch

只是从react-router中导入Switch组件,然后重新导出而已。

Router、Route也是如此。 react-router-dom依赖react-router,所以我们使用npm安装依赖的时候,只需要安装相应环境下的库即可,不用再显式安装react-router。基于浏览器环境的开发,只需要安装react-router-dom