造轮子——前端路由

阅读数:1 2020 年 4 月 3 日 17:18

造轮子——前端路由

前端路由

现代前端开发中最流行的页面模型,莫过于 SPA 单页应用架构。单页面应用指的是应用只有一个主页面,通过动态替换 DOM 内容并同步修改 url 地址,来模拟多页应用的效果,切换页面的功能直接由前台脚本来完成,而不是由后端渲染完毕后前端只负责显示。前端三驾马车 Angular,Vue,React 均基于此模型来运行的。SPA 能够以模拟多页面应用的效果,归功于其前端路由机制。

前端路由,顾名思义就是一个前端不同页面的状态管理器, 可以不向后台发送请求而直接通过前端技术实现多个页面的效果。angularjs 中的 ui-router,vue 中的 vue-router, 以及 react 的 react-router 均是对这种功能的具体实现。

既然前端路由这么牛逼,那必须的好好研究一下。

两种实现方式及其原理

常见的路由插件中两种方式都是支持且可以切换的,例如 angularjs1.x 中就可以通过以下代码从 Hash 模式切换到 H5 模式:

$locationProvider.html5Mode(true);

切换到 HTML5 的路由模式,主要用于避免 url 地址中包含#而引发的问题。

HashChange

  • 原理
    HTML 页面中通过锚点定位原理可进行无刷新跳转,触发后 url 地址中会多出# + 'XXX’的部分,同时在全局的 window 对象上触发 hashChange 事件,这样在页面锚点哈希改变为某个预设值的时候,通过代码触发对应的页面 DOM 改变,就可以实现基本的路由了, 基于锚点哈希的路由比较直观,也是一般前端路由插件中最常用的方式。

  • 应用
    下面通过一个实例看一下, 当点击 angularjs 的连接时,可以看到控制台打印出了相应的信息。

造轮子——前端路由
造轮子——前端路由

HTML5 HistoryAPI

  • 原理
    HTML5 的 History API 为浏览器的全局 history 对象增加的扩展方法。一般用来解决 ajax 请求无法通过回退按钮回到请求前状态的问题。

在 HTML4 中,已经支持 window.history 对象来控制页面历史记录跳转,常用的方法包括:

复制代码
history.forward(); // 在历史记录中前进一步
history.back(); // 在历史记录中后退一步
history.go(n): // 在历史记录中跳转 n 步骤,n=0 为刷新本页,n=-1 为后退一页。
在 HTML5 中,window.history 对象得到了扩展,新增的 API 包括:
history.pushState(data[,title][,url]);// 向历史记录中追加一条记录
history.replaceState(data[,title][,url]);// 替换当前页在历史记录中的信息。
history.state;// 是一个属性,可以得到当前页的 state 信息。
window.onpopstate;// 是一个事件,在点击浏览器后退按钮或 js 调用 forward()、back()、go() 时触发。回调函数中可传入一个 event 对象,event.state 即为通过 pushState() 或 replaceState() 方法传入的 data 参数。
  • 应用
    浏览器访问一个页面时,当前地址的状态信息会被压入历史栈, 当调用 history.pushState() 方法向历史栈中压入一个新的 state 后,历史栈顶部的指针是指向新的 state 的。可以将其作用简单理解为假装已经修改了 url 地址并进行了跳转 , 除非用户点击了浏览器的前进, 回退, 或是显式调用 HTML4 中的操作历史栈的方法,否则不会触发全局的 popstate 事件。

在下面的示例中,点击导航按钮,可以看到 url 地址栏发生了变化,且控制台打印出了响应的信息。

造轮子——前端路由
造轮子——前端路由

hash 和 history API 对比
造轮子——前端路由

亲手造一个简单的前端路由插件

造轮子, 不是为了把它装在你的车上,而是当你在荒郊野外开车而轮子出了问题时多一种选择。
接下来就自己动手实现一个前端路由的插件吧~

  • 基于 Hash 的前端路由插件 myHashRouter.js

我们希望实现的功能是:
1. 引入 MyHashRouter.js 库
2. 通过 when() 方法来定义若干不同的路由状态
3. 通过 init() 方法启动路由功能
4. 通过点击导航实现前端路由切换

首先编写 js 骨架,如下所示:
造轮子——前端路由

完成了路由插件的编写后,我们在 demo 中引入该库,然后使用 when() 方法注册几个路由地址,再使用 init() 方法启动路由,脚本部分代码如下:

造轮子——前端路由

效果:运行附件中的 router-demo-hash.html, 点击导航按钮,即可看到 url 地址栏以及内容区域同步更改。

  • 基于 History API 的前端路由插件 myHistoryRouter.js
    由于 History API 不支持低于 IE10 以下版本的浏览器(其他大多数现代浏览器基本都支持), 所以我们在 init() 方法启动时先进行可用性判断,基本代码框架与基于 Hash 的路由插件一致。每个方法的实现并不难写,这里不再赘述,笔者自己的代码实现放在附件 myHistoryRouter.js 中,水平有限,仅供参考。

效果:

造轮子——前端路由

  • 集成说明
    为方便理解,本例中将两种模式分开编写,如果是插件库的开发,可以模仿 ui-router 增加一个 html5mode() 的方法, 在 init() 方法启动路由时,根据所传的参数生成不同的路由插件的单例,也就是我们常说的工厂模式来实现即可。

后记

造车轮是一个很好的学习方式,虽然自己造的车轮很简陋,但是对于理解工具的底层原理却很有帮助。

本例只是编写了一个路由工具的基本骨架,真正的路由工具还需要做很多功能扩展,个别功能的复杂度也会很高,例如路径的正则匹配, 懒加载, 组合视图, 嵌套视图, 路由动画等等,有兴趣的小伙伴可以在本例提供的框架上进行学习扩展。

附件

myHashRouter.js 源码:
造轮子——前端路由

本文转载自华为云产品与解决方案公众号。

原文链接: https://mp.weixin.qq.com/s/tsKI3s9G7lSwH0QwyGBgWA

评论

发布