URI & URL
URI,Uniform Resource Identifier,統(tǒng)一資源標(biāo)識符。
URL,Uniform Resource Location,統(tǒng)一資源定位符。
URI 簡單來理解就是標(biāo)識/定義了一個資源,而 URL 在定義/標(biāo)識資源的同時還需要描述如何訪問到該資源??梢哉J(rèn)為 URL 是 URI 的一個子集。
舉個例子:
公司里每個人都有一個內(nèi)部唯一的花名,這個花名其實就可以認(rèn)為是 URI,它對應(yīng)了公司內(nèi)部唯一的一個人(資源)。當(dāng)我需要找這個人時,雖然我知道了花名(URI),但是并找不到他人,因為我不知道他的工位,這時候就需要知道他的工位號如 13B-11 ,工位號+花名其實就是一個 URL,它指定了一個人以及怎么找到這個人的位置。
上述例子可能并不規(guī)范,但是感覺這樣比較容易理解區(qū)分。一般來說 URI 有一個通用的結(jié)構(gòu)描述:
scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

其實現(xiàn)在我們不必刻意去區(qū)分 URI / URL / URN 。在 [RFC3986]上已經(jīng)明確說明這個點:
Future specifications and related documentation should
use the general term "URI" rather than the more restrictive terms
"URL" and "URN".
fragment
主要資源是由 URI 進行標(biāo)識,URI 中的 fragment 用來標(biāo)識次級資源。我理解看來,fragment 主要是用來標(biāo)識 URI 所標(biāo)識資源里的某個資源。
在 URI 的末尾通過 hash mark(#)作為 fragment 的開頭,其中 # 不屬于 fragment 的值。
https://domain/index#L18
這個 URI 中 L18 就是 fragment 的值。這有哪些特殊的地方呢?
-
#有別于?,?后面的查詢字符串會被網(wǎng)絡(luò)請求帶上服務(wù)器,而 fragment 不會被發(fā)送的服務(wù)器; - fragment 的改變不會觸發(fā)瀏覽器刷新頁面,但是會生成瀏覽歷史;
- fragment 會被瀏覽器根據(jù)文件媒體類型(MIME type)進行對應(yīng)的處理;
- Google 的搜索引擎會忽略
#及其后面的字符串。
針對以上幾點特性,分別介紹下 URI fragment 的應(yīng)用。
特性 1 & 2 單頁面路由
針對 1、2 這兩個特性,目前主要的應(yīng)用就是單頁面路由。具體原理簡單描述如下:
JavaScript 提供了 location.hash 來操作當(dāng)前 URI 的 fragment,同時提供了 Hashchange 事件監(jiān)聽 fragment 的變化。利用這兩個 API 再結(jié)合上述特性 1、2 就可以實現(xiàn)一個簡單前端路由。具體流程如下圖:

修改 location.hash 值,觸發(fā) hashchange 事件,JS 處理對應(yīng)的邏輯,改變頁面 UI 實現(xiàn)頁面的跳轉(zhuǎn),并在瀏覽器中產(chǎn)生歷史記錄。
具體代碼實現(xiàn)可以參考 vue-router hash 模式下的實現(xiàn) [https://github.com/vuejs/vue-router/blob/dev/src/history/hash.js]。
hashchange event 目前的兼容性表現(xiàn)較好,主流瀏覽器都支持【[caniuse]】。實現(xiàn)單頁面路由還會使用 H5 為 history 新增的兩個 API
History.pushState() & Histroy. replaceState ()。雖然在 URL 美觀度上很令人滿意,但是也存在缺點,如兼容性表現(xiàn)、對 Web 容器配置的額外要求 —— 直接使用 pushState 的 URL 訪問應(yīng)用,則會 404,需要 Web 容器對這些 URL 進行處理,重寫到對應(yīng)的 HTML 頁面上。具體代碼實現(xiàn)可以參考 vue-router histroy 模式下的實現(xiàn) [https://github.com/vuejs/vue-router/blob/dev/src/history/html5.js]。
特性 3 HTML 錨點
在 HTML 中比較常見的一個應(yīng)用 —— 頁面內(nèi)定位。在頁面中通過設(shè)置標(biāo)簽的 id 屬性來定義錨點,從而實現(xiàn)錨點定位。實際上錨點定位的實現(xiàn)正是依賴了 fragment 的特性 3。如這個 URI https://domain/index.html#L18,假設(shè)返回的文件類型是 text/html,則瀏覽器會讀取 URI’s fragment,然后在頁面中尋找 L18 這個錨點,并將頁面滾動到該錨點的位置。
因此我們當(dāng)點擊 <a href="#top">top</a>時,實際上處理過程是 URI 的 hash 發(fā)生變化,然后瀏覽器讀取新的 fragment,并尋找 DOM 中是否存在對應(yīng)的錨點,將該錨點顯示到頁面中。
在 MIME Type 為 HTML 或 XML 時,如https://domain/index.html#這個 URI 中是空的 fragment,則瀏覽器默認(rèn)顯示頁面的最頂端。
URI 對應(yīng)的資源類型不同,瀏覽器對該 URI’s fragment 的處理方式就不一樣,具體不同類型的處理方式可以參閱:[https://en.wikipedia.org/wiki/Fragment_identifier#Examples] 。
特性 4
特性4 其實是針對 hash 模式前端路由來說的一個缺點。因為 fragment 會被 Google 搜索引擎忽略掉,因此對于用 hash 模式前端路由的應(yīng)用的 SEO 來說是很不友好的。不過 Google 給了一個方案,就是在 # 緊跟一個 ! ,這樣Google 搜索引擎就會將這個 URI 進行轉(zhuǎn)換,如 https://domain/index.html#!L18轉(zhuǎn)換后就成為了 https://domain/index.html?_escaped_fragment_=L18。這樣搜索引擎就會攜帶上 URI’s fragment 直接去訪問這個 URI,開發(fā)者可以利用這個 trick 優(yōu)化網(wǎng)站的 SEO。
小結(jié)
- fragment 對于 HTML 文檔來說就是頁面內(nèi)的定位標(biāo)識符,可以實現(xiàn) HTML 頁面內(nèi)的定位。當(dāng)然瀏覽器針對不同類型的資源會有區(qū)分的處理 fragment;
- 利用 fragment 實現(xiàn)前端頁面無刷新修改 Brower’s URI;
- 根據(jù)搜索引擎規(guī)則,可以優(yōu)化無刷新修改頁面的 SEO;
對于 URI’s fragment 的探究也就到這里,附上下關(guān)的參考 Links: - [http://www.ietf.org/rfc/rfc3986.txt]
- [https://en.wikipedia.org/wiki/Fragment_identifier]
- [http://www.ruanyifeng.com/blog/2011/03/url_hash.html]