当前位置: 首页 > Web前端 > HTML5

国内存在感最低的前端API——浏览器路由

时间:2023-04-05 14:52:53 HTML5

latest一直在翻看Vue和React路由的相关知识,最后发现这些路由框架的模块功能的实现都是基于浏览器的本机路由API。本着追根溯源的初衷,想整体梳理一下浏览器原生的路由API,以便更顺利的理解Vue-Router和React-Router的相关实现和原理。后台浏览器的主要功能是根据输入的URL在窗口中加载相应的文档。同时,浏览器会在一个标签窗口中记录所有加载的文档,并提供“前进”、“后退”和“刷新”功能,用户可以在这些记录的文档之间切换,重新加载当前页面获取最新浏览信息。这些功能的实现首先是在服务器端实现的,因为当时的引用没有前后端分离,页面内容也是动态生成的,所以这些页面的跳转、切换、刷新都是全部在服务器端实现。后来出现了SPA(SinglePageApplication)。页面通过JavaScript动态生成加载到页面中,无需刷新即可加载页面的最新状态信息。这时候如果要提供以上功能需要自己处理(因为此时的页面都在同一个大框架页面中,根本没有页面跳转切换),所以Router实现对应各种框架已经诞生。在浏览器中实现前端路由主要有两种方式:一种是我们常用的hash,一种是HTML5提供的history。事实上,Node.js服务器端还有另一种使用堆栈的方法。在这里,我们将重点关注浏览器提供的哈希和历史记录。stack是如何实现的,上一次讲x-Router源码的时候会详细讲到。hash在浏览器的URL地址栏,我们总会找到这样一个地址:react.docschina.org/docs/react-...React官网关于lazy的地址)。想必大家已经发现了:在这串url的末尾有一串#开头的标志,那么它到底起到了什么样的作用呢?绝对不会无缘无故出现的。借助哈希功能,您可以直接在浏览器中打开此链接地址。您是否发现该页面会自动滚动到(位于页面顶部)标题为React.lazy的文档的一部分。再往上翻页面,肯定会发现上面还有一些文档内容。此时,你将地址栏的地址修改为react.docschina.org/docs/react-...React.Suspense部分。在早期,散列被用作URL的一部分来定位文档中的文档片段。在上面的示例中,我们通过在URL末尾添加#reactlazy和#reactsuspense来定位文档中标题为React.lazy和React.Suspense的部分。那么他们到底是怎么做到的呢?通过查看元素,我们发现在React.lazy和React.Suspense对应的title部分分别有一个h3标签,标签的id属性对应我们在URL地址栏输入的hash值(只是漏了标志)。可能有同学对hash定位文档片段有疑惑:hash为什么要通过元素上的id属性来定位文档呢?正如我们前面提到的,URL的哈希部分用于在文档中定位文档片段。想一想:需要定位的文档片段必须是唯一的,否则定位肯定不准确,那这个定位文档就有点鸡肋了,文档中唯一标识的属性是id,如果是我,我也的文档将通过哈希匹配元素的id来定位。现在来验证一下我们的猜想:1.首先在新的tab窗口打开react.docschina.org/docs/react-...页面,然后在audit元素下找到上图所示的DOM元素,修改其中的h3标签它的id属性值为reactlazy1,然后在URL地址栏中加上#reactlazyhash值并回车。此时页面没有定位到标题为React.lazy的文档片段,最后将URL地址栏中的#reactlazy哈希值改为输入#reactlazy1哈希值并回车。此时页面并没有定位到名为React.lazy的文档片段。这一系列的表现说明hash位置还是和元素的id属性值有关;2.还是在新的tab窗口打开react.docschina.org/docs/react-...页面,然后手动滚动页面到标题为React.lazy的文档片段,鼠标放在上面会出现一个锚点图标标题。点击图标发现页面定位到标题为React.lazy的文档片段,URL地址栏变为react.docschina.org/docs/react-...#reactlazy哈希值。这时候回过头来看我们之前给的截图,发现id属性值为reactlazy的h3标签有一个href属性值为#reactlazy的a标签。其实我们在页面上看到的锚点图标就是a标签。展示。当我们点击锚点图标时,我们点击a链接,然后定位到id属性值为reactlazy的h3标签的url,很好的说明hash位置还是和元素的id属性值有关;3.MDN官方定义如下:MDN官方文档中有明确的定义,但是我们还是通过两个方面来证明我们的推论。乍一看,好像说了很多没用的话。其实,这样的反复推敲,更有利于我们深入理解相关知识点,为什么是这样,不是那样!hashroutehash不仅可以通过设置文档中元素的ID来定位文档片段,还可以设置为任意字符串来表示路由。在Vue、React等现代前端框架中,为了实现功能齐全的SPA应用,都配备了相应的路由系统。这些路由系统都提供哈希路由模式。在哈希模式下,哈希将支持任意字符串来表示对应的URL。这些路由系统对于hash模式的实现基本相同:在设置location的值之后。更新自己的状态。支持HTML5的浏览器一旦发现fragmentidentifier发生变化,就会触发Window对象上的hashchange事件,进而触发对象的函数处理逻辑——解析location.hash的值,然后使用该值包含state重新呈现应用程序的信息。这里只是一个基本思路,路由系统的具体实现会在后面讲解!hashevent//监听window下的hashchange事件window.onhashchange=function(){//触发事件时输出当前hash值console.log(window.location.hash)}在不支持HTML5的浏览器中,我们可以通过100ms的轮询来模拟监听url变化:(function(window){//如果浏览器不支持原生实现的事件,则开始模拟,否则退出。如果(window.document.body中的“onhashchange”)返回;varlocation=window.location,oldUrl=location.href,oldHash=location.hash;//每100ms检测一次hash是否变化setInterval(function(){varnewUrl=location.href,newHash=location.hash;//hash变化,全局注册onhashchange方法(这个名字要和模拟事件名称);if(newHash!==oldHash&&typeofwindow.onhashchange==="function"){//执行方法window.onhashchange({type:"hashchange",oldURL:oldUrl,newURL:newUrl});oldUrl=newUrl;oldHash=newHash;}},100);})(window)??注意:设置location.hash属性会更新地址栏中显示的URL,并在浏览器的历史记录中添加一条记录。History为了规范浏览器历史管理的管理,HTML5定义了一个相对复杂的API——history。Historyapi1和history添加了两个新的API,history.pushState()和history.replaceState()。两个API都接受相同的参数:它们的区别在于:history.pushState()方法是将新状态添加到浏览器的历史记录中,也就是说,通过单击“返回”按钮,返回到上一个页面;history.replaceState()是将当前的历史状态替换为新的状态,也就是说没有更多的历史记录,“后退”按钮不能操作,页面不能“后退”。??注意:执行这两个API时,浏览器的URL地址栏会发生变化,但不会刷新页面内容!StateObject(state):**一个JavaScript对象,包含恢复当前文档所需的所有信息。可以是任何可以通过JSON.stringify()方法转换成对应字符串形式的对象,也可以是其他特定的局部类型,如Date、RegExp。Title(title):**浏览器可以用它来识别浏览历史中保存的状态。您可以传递一个空字符串或一个简短的标题来指示要进入的状态。地址(URL):**用于表示当前状态所在的位置。新URL不必是绝对路径;如果是相对路径,会以当前的URL为准(像hashlike#reactlazy);传入的URL应与当前URL具有相同的来源,否则pushState()将抛出异常。这个参数是可选的;如果未指定,则为文档的当前URL。为此,我们可以利用语雀官网做一系列实验:window.history.pushState(null,null,"https://www.yuque.com/dashboard/?name=littleLane");//结果:https://www.yuque.com/dashboard/?name=littleLanewindow.history.pushState(null,null,"https://www.yuque.com/dashboard/name/littleLane");//结果:https://www.yuque.com/dashboard/name/littleLanewindow.history.pushState(null,null,"?name=littleLane");//result:https://www.yuque.com/dashboard?name=littleLanewindow.history.pushState(null,null,"name=littleLane");//结果:https://www.yuque.com/dashboard/name=littleLanewindow.history.pushState(null,null,"/name/littleLane");//result:https://www.yuque.com/dashboard/name/littleLane在控制台执行上面这一系列语句时,浏览器的URL变成了我们记下的结果,但是页面并没有重新render,每次执行pushState都会在浏览器历史记录中添加一条记录,可以通过“返回”按钮查看。执行上面的测试语句后,可以将pushState替换为replaceState,再进行一轮测试。此时,新的浏览记录将替代当前的历史记录,您仍然可以通过“返回”按钮查看。??注意:这里的url不支持跨域。当我们把www.yuque.com替换成www.baidu.com时,会报错。2、history对象除了上面新增的API外,还有一个length属性表示浏览历史列表的个数,还定义了back()、forward()、go()方法来切换浏览记录。History对象的back()和forward()方法与浏览器的“后退”和“前进”按钮做同样的事情:它们使浏览器在浏览历史记录中后退或前进一个空间。go()方法接受一个整数,并且可以在浏览历史列表中向前(接受正整数参数)或向后(接受负整数参数)跳过任意数量的页面。比如history.go(-1)会往回跳一页,history.go(0)会刷新当前页,history.go(1)会往前跳一页。history事件-popstate当用户通过“前进”和“后退”按钮浏览已保存的历史状态时,浏览器会在Window对象上触发popstate事件。与此事件关联的事件对象具有一个状态属性,该属性包含传递给pushState()方法的状态对象的副本。//监听窗口下的onpopstate事件window.onpopstate=function(state){//当onpopstate事件(用户通过“前进”和“后退”按钮切换浏览记录)被触发时,输出当前状态console.log(state)}LocationWindow对象的location属性和Document对象的location属性指的是Location对象,用来表示窗口当前显示的文档的URL,定义了一个方法来使窗口加载一个新文档。window.location===document.location//总是返回true来解析URLLocation对象的href属性是一个代表当前URL完整文本的字符串。Location对象的toString()方法返回href属性的值,因此在隐式调用toString()的情况下,可以使用location代替location.href。对象的协议、主机、主机名、端口、路径名和搜索代表URL的一部分,因此称为URL解析属性。一般我们使用较多的是提取URL中的参数://获取地址栏参数constgetUrlParam=(paramName)=>{consturlParams={};让参数=window.location.search.substring(1);如果(!params){返回;}params=params.split('&');for(leti=0;i