冥思苦想仍是決定再加一章,這章主要介紹路由的原理實現,而且會使用代碼模擬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.