写点什么

造轮子——前端路由

  • 2020-04-03
  • 本文字数:2036 字

    阅读完需:约 7 分钟

造轮子——前端路由

前端路由

现代前端开发中最流行的页面模型,莫过于 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


2020-04-03 17:18903

评论

发布
暂无评论
发现更多内容

编写代码最应该做好的事情是什么?

李子捌

Java 28天写作 21天挑战 12月日更

53 K8S之资源监控与资源指标

穿过生命散发芬芳

k8s 28天写作 12月日更

架构实战营-模块七

瓜子葫芦侠

「架构实战营」

架构师实战营 - 模块7 - 作业

lucian

架构师实战营

架构训练营 - 模块 7 作业

焦龙

架构训练营

LabVIEW读写各类格式图像的方法(基础篇—1)

不脱发的程序猿

机器视觉 图像处理 LabVIEW 读写各类格式图像的方法

释放协同价值——元宇宙中的区块链

CECBC

MySQL的int (10) 和 int (11) 的区别

JavaEdge

12月日更

圣迪

监控的黄金指标有哪些

耳东@Erdong

监控 28天写作 指标 12月日更

有向无环图在新增节点时的环状检测

waitmoon

DAG

Redis RDB 持久化详解

程序员历小冰

redis 持久化 28天写作 12月日更

谈数字人民币对我国金融系统的双重影响

CECBC

架构实战营模块七作业

渐行渐远

架构实战营

Golang中数字与字符串的转换

liuzhen007

28天写作 12月日更

王者荣耀商城异地多活架构设计

Geek_cb2b43

王者荣耀商城异地多活架构设计

Steven

架构实战课

.NET6东西--可写的JSON DOM API

喵叔

28天写作 12月日更

模块7作业

小何

「架构实战营」

严管之下,还有谁在“挖矿”?

CECBC

如何在CRM WebClient UI里使用HANA Live Report

汪子熙

报表 CRM 28天写作 hana 12月日更

模块7作业

忘记喝水的猫

架构训练营

企业智能转型对AI技术的挑战及应对,答案是MLOps

第四范式开发者社区

机器学习 OpenMLDB 特征平台 MLOps

绩效沟通的案例分享

搬砖的周狮傅

绩效管理

销售易携手DataPipeline,推动“实时感知-主动决策-客户成功”的变革!

DataPipeline数见科技

大数据 中间件 数据融合 数据迁移 数据管理

如何真正学习Go 语言

宇宙之一粟

golang 学习方法 Go 语言 12月日更

apacheunomi漏洞介绍及代码分析

网络安全学海

黑客 网络安全 信息安全 渗透测试 安全漏洞

模块七作业

心怀架构

人生最重要的是快乐,静态的快乐

mtfelix

28天写作

读《软件工程之美》之02

williamcai

软件工程

架构实战营-模块八

瓜子葫芦侠

「架构实战营」

造轮子——前端路由_文化 & 方法_华为云产品与解决方案_InfoQ精选文章