一篇文章,不可能做的面面俱到,全部閱聽人。希望大家帶着發散思維去看文章,将文章涉及的知識點,吸收為己所用。這樣看完一篇文章,才能有所收獲。
好了不裝了,今天我就化身性感面試官線上問大家一個問題,“談談你對前端路由的了解”。看到這個問題,那回答可多了去了。但是換位思考一下,你問候選人這個問題的時候,你想要得到什麼答案?以我個人拙見,我希望候選人能從全局解讀這個問題,大緻以下三點。
1、為什麼會出現前端路由。
2、前端路由解決了什麼問題。
3、前端路由實作的原理是什麼。
我們帶着這三個問題,繼續往下看,閱讀的過程中如果同學們有自己的見解,可以評論區發表自己的看法。如果覺得講的内容讓你有了新的見解,請獻上你寶貴的一贊????,這将是我繼續寫作的動力。
這裡不糾結叫法,凡是整個項目都是 <code>DOM</code> 直出的頁面,我們都稱它為“傳統頁面”(其 屬于首屏直出,這裡我不認為是傳統頁面的範疇)。那麼什麼是 <code>DOM</code> 直出呢?簡單說就是在浏覽器輸入網址後發起請求,傳回來的 <code>HTML</code> 頁面是最終呈現的效果,那就是 <code>DOM</code> 直出。并且每次點選頁面跳轉,都會重新請求 <code>HTML</code> 資源。耳聽為虛,眼見為實。我們以這個位址為例,驗證以下上述說法。

定眼一看,就能明白上圖在描述什麼。沒錯,部落格園就是一個傳統頁面搭建而成的網站,每次加載頁面,都會傳回 <code>HTML</code> 資源以及裡面的 <code>CSS</code> 等靜态資源,組合成一個新的頁面。
“瞎了”的同學,我再教一個方法,就是在浏覽器頁面右鍵,點選“顯示網頁源代碼”,打開後如下所示:
網頁上能看到什麼圖檔或文字,你能在上述圖檔中找到相應的 <code>HTML</code> 結構,那也屬于傳統頁面,也就是 <code>DOM</code> 直出。
時代在進步,科技在發展,面對日益增長的網頁需求,網頁開始走向子產品化、元件化的道路。随之而來的是代碼的難以維護、不可控、疊代艱難等現象。面臨這種情況,催生出不少優秀的現代前端架構,首當其沖的便是 <code>React</code> 、 <code>Vue</code> 、 <code>Angular</code> 等著名單頁面應用架構。而這些架構有一個共同的特點,便是“通過 JS 渲染頁面”。
舉個例子,以前我們直出 <code>DOM</code> ,而現在運用這些單頁面架構之後, <code>HTML</code> 頁面基本上隻有一個 <code>DOM</code> 入口,大緻如下所示:
所有的頁面元件,都是通過運作上圖底部的 <code>app.js</code> 腳本,挂載到 <code><div id="root"></div></code> 這個節點下面。用一個極其簡單的 JS 展示挂載這一個步驟:
image.png
脫去所有的凡塵世俗,最本真的單頁項目運作形式便是如此。注意,我要點題了啊!!!
既然單頁面是這樣渲染的,那如果我有十幾個頁面要互相跳轉切換,咋整!!??這時候 前端路由 應運而生,它的出現就是為了解決單頁面網站,通過切換浏覽器位址路徑,來比對相對應的頁面元件。我們通過一張醜陋的圖檔來了解這個過程:
前端路由 會根據浏覽器位址欄 <code>pathname</code> 的變化,去比對相應的頁面元件。然後将其通過建立 <code>DOM</code> 節點的形式,塞入根節點 <code><div id="root"></div></code> 。這就達到了無重新整理頁面切換的效果,從側面也能說明正因為無重新整理,是以 <code>React</code> 、 <code>Vue</code> 、 <code>Angular</code> 等現代架構在建立頁面元件的時候,每個元件都有自己的 生命周期 。
前端路由 插件比較火的倆架構對應的就是 <code>Vue-Router</code> 和 <code>React-Router</code> ,但是它們的邏輯,歸根結底還是一樣的,用殊途同歸四個字,再合适不過。
通過分析哈希模式和曆史模式的實作原理,讓大家對前端路由的原理有一個更深刻的了解。
<code>a</code> 标簽錨點大家應該不陌生,而浏覽器位址上 <code>#</code> 後面的變化,是可以被監聽的,浏覽器為我們提供了原生監聽事件 <code>hashchange</code> ,它可以監聽到如下的變化:
點選 <code>a</code> 标簽,改變了浏覽器位址
浏覽器的前進後退行為
通過 <code>window.location</code> 方法,改變浏覽器位址
接下來我們利用這些特點,去實作一個 <code>hash</code> 模式的簡易路由:線上運作
當然,這是很簡單的實作,真正的 hash 模式,還要考慮到很多複雜的情況,大家有興趣就去看看源碼。
浏覽器展示效果如下:
<code>history</code> 模式會比 <code>hash</code> 模式稍麻煩一些,因為 <code>history</code> 模式依賴的是原生事件 <code>popstate</code> ,下面是來自 MDN 的解釋:
小知識:pushState 和 replaceState 都是 HTML5 的新 API,他們的作用很強大,可以做到改變浏覽器位址卻不重新整理頁面。這是實作改變位址欄卻不重新整理頁面的重要方法。
包括 <code>a</code> 标簽的點選事件也是不會被 <code>popstate</code> 監聽。我們需要想個辦法解決這個問題,才能實作 <code>history</code> 模式。
**解決思路:**我們可以通過周遊頁面上的所有 <code>a</code> 标簽,阻止 <code>a</code> 标簽的預設事件的同時,加上點選事件的回調函數,在回調函數内擷取 <code>a</code> 标簽的 <code>href</code> 屬性值,再通過 <code>pushState</code> 去改變浏覽器的 <code>location.pathname</code> 屬性值。然後手動執行 <code>popstate</code> 事件的回調函數,去比對相應的路由。邏輯上可能有些饒,我們用代碼來解釋一下:線上位址
這裡注意,不能在浏覽器直接打開靜态檔案,需要通過 web 服務,啟動端口去浏覽網址。
這篇文章主要知識點集中在前端路由這塊,能完全看完,并且把實作原理捋一遍,我想你應該對現代前端架構會有一個新的了解。沒有新的了解的同學,來杭州打我,我不還手。
作者:尼克陳
- EOF -
推薦閱讀 點選标題可跳轉
JavaScript閉包和匿名函數的關系詳解