冥思苦想仍是决定再加一章,这章主要介绍路由的原理实现,而且会使用代码模拟VueRouter的原理。我学习前端的感受是,框架有不少,会用就能够。但设计的思想却值得咱们去研究,由于这才是框架的灵魂,以及真正的智慧所在。其实在使用框架的时候咱们不难发现,有些命令或者方法咱们用的多了甚至能猜出它的设计思想以及实现原理。因此熟练使用一个框架或许并不难,但若是不能懂得它设计的初衷以及其中的奥妙,就老是会以为缺乏一些什么。废话少说,进入正题。前端
先看看这个:https://blog.csdn.net/fifteen...vue
引用其中的一段话,关于hash和history的区别(划重点,要考!):react
hash模式url里面永远带着#号,咱们在开发当中默认使用这个模式。那么何时要用history模式呢?若是用户考虑url的规范那么就须要使用history模式,由于history模式没有#号,是个正常的url适合推广宣传。固然其功能也有区别,好比咱们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react作的,我们把这个页面分享到第三方的app里,有的app里面url是不容许带有#号的,因此要将#号去除那么就要使用history模式,可是使用history模式还有一个问题就是,在访问二级页面的时候,作刷新操做,会出现404错误,那么就须要和后端人配合让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上就ok啦。
VueRouter拥有两种模式,那就是咱们熟悉的hash和history,因此在设计时也要将这两种不一样的模式都考虑进来。代码以下:
hash:nginx
<body> <!-- a标签 = router-link --> <a href="#/">首页</a> <a href="#/about">关于</a> <!-- div = router-view --> <div id="view"></div> <script> const vw = document.getElementById("view") window.addEventListener("hashchange", () => { if (location.hash == "#/") { vw.innerHTML = "我是首页" } else if (location.hash == "#/about") { vw.innerHTML = "我是关于" } }) </script> </body>
看看效果:
那么问题来了,router-link的tag属性能够改变显示的标签。也就是说若是不是默认的a标签该怎么作呢?继续改进,代码以下:apache
<body> <!-- 任意标签 = router-link --> <span style="color: deepskyblue;cursor:pointer;" onclick="changeHash('#/')">首页</span> <span style="color: deepskyblue;cursor:pointer;" onclick="changeHash('#/about')">关于</span> <!-- div = router-view --> <div id="view"></div> <script> function changeHash (path) { location.hash = path } window.addEventListener("hashchange", () => { if (location.hash == "#/") { view.innerHTML = "我是首页" } else if (location.hash == "#/about") { view.innerHTML = "我是关于" } }) </script> </body>
看看效果:
解决方案就是咱们能够经过为任意标签添加点击事件,并在点击时传入对应的hash值,而后在点击事件处理函数中改变地址栏的hash值。后端
能够看到,使用a标签和div模拟的vueRouter,彷佛还像那么回事儿。因此hash模式下路由的原理也显而易见,说白了就是:浏览器
监听hashchange事件,而后根据地址栏上的路径,进行DOM渲染。
history:app
<body> <!-- 任意标签 = router-link --> <span style="color: deepskyblue;cursor:pointer;" onclick="changeHash('/')">首页</span> <span style="color: deepskyblue;cursor:pointer;" onclick="changeHash('/about')">关于</span> <!-- div = router-view --> <div id="view"></div> <script> function changeHash (path) { history.pushState(null, null, path) if (location.pathname == "/") { view.innerHTML = "首页是我" } else if (location.pathname == "/about") { view.innerHTML = "关因而我" } } </script> </body>
看看效果:
能够看到浏览器的回退和前进并无效果,这也是与hash模式不一样的地方。改进:框架
<body> <!-- 任意标签 = router-link --> <span style="color: deepskyblue;cursor:pointer;" onclick="changeHash('/')">首页</span> <span style="color: deepskyblue;cursor:pointer;" onclick="changeHash('/about')">关于</span> <!-- div = router-view --> <div id="view"></div> <script> function changeHash (path) { history.pushState(null, null, path) if (location.pathname == "/") { view.innerHTML = "首页是我" } else if (location.pathname == "/about") { view.innerHTML = "关因而我" } } window.addEventListener("popstate", () => { if (location.pathname == "/") { view.innerHTML = "首页是我" } else if (location.pathname == "/about") { view.innerHTML = "关因而我" } }) </script> </body>
看看变化:
那么其实history模式下的路由原理其实也能够总结出来了:
使用history.pushState API来切换地址栏的路径,再经过监听popstate事件来操做浏览器的回退和前进按钮。函数
代码没什么可讲的,着重说说hash和history各自的特色。
原本想着这一章把原理的内容都写完,但实在是模拟路由的内容太多了,仍是放在后面一点一点写完吧。本章已经将路由的原理带出来了,后面的内容会相对初级的实现路由。
Keep foolish, keep hungry.