前端路由的兩種實現原理

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

History API

這里不細說每一個 API 的用法,大家可以看 MDN 的文檔:https://developer.mozilla.org...

重點說其中的兩個新增的API?history.pushState?和?history.replaceState

這兩個 API 都接收三個參數,分別是

  • 狀態對象(state object)?— 一個JavaScript對象,與用pushState()方法創建的新歷史記錄條目關聯。無論何時用戶導航到新創建的狀態,popstate事件都會被觸發,并且事件對象的state屬性都包含歷史記錄條目的狀態對象的拷貝。

  • 標題(title)?— FireFox瀏覽器目前會忽略該參數,雖然以后可能會用上。考慮到未來可能會對該方法進行修改,傳一個空字符串會比較安全。或者,你也可以傳入一個簡短的標題,標明將要進入的狀態。

  • 地址(URL)?— 新的歷史記錄條目的地址。瀏覽器不會在調用pushState()方法后加載該地址,但之后,可能會試圖加載,例如用戶重啟瀏覽器。新的URL不一定是絕對路徑;如果是相對路徑,它將以當前URL為基準;傳入的URL與當前URL應該是同源的,否則,pushState()會拋出異常。該參數是可選的;不指定的話則為文檔當前URL。

相同之處是兩個 API 都會操作瀏覽器的歷史記錄,而不會引起頁面的刷新。

不同之處在于,pushState會增加一條新的歷史記錄,而replaceState則會替換當前的歷史記錄。

我們拿大百度的控制臺舉例子(具體說是我的瀏覽器在百度首頁打開控制臺。。。)

我們在控制臺輸入

window.history.pushState(null,?null,?"https://www.baidu.com/?name=orange");

好,我們觀察此時的 url 變成了這樣

我們這里不一一測試,直接給出其它用法,大家自行嘗試

window.history.pushState(null, null, "https://www.baidu.com/name/orange");
//url: https://www.baidu.com/name/orangewindow.history.pushState(null, null, "?name=orange");
//url: https://www.baidu.com?name=orangewindow.history.pushState(null, null, "name=orange");
//url: https://www.baidu.com/name=orangewindow.history.pushState(null, null, "/name/orange");
//url: https://www.baidu.com/name/orangewindow.history.pushState(null, null, "name/orange");
//url: https://www.baidu.com/name/orange

注意:這里的 url 不支持跨域,當我們把?www.baidu.com?換成?baidu.com?時就會報錯。

Uncaught DOMException: Failed to execute?'pushState'?on?'History': A history state object?with?URL?'https://baidu.com/?name=orange'?cannot be created?in?a?document?with?origin?'https://www.baidu.com'?and URL?'https://www.baidu.com/?name=orange'.

回到上面例子中,每次改變 url 頁面并沒有刷新,同樣根據上文所述,瀏覽器會產生歷史記錄

這就是實現頁面無刷新情況下改變 url 的前提,下面我們說下第一個參數?狀態對象

如果運行?history.pushState()?方法,歷史棧對應的紀錄就會存入?狀態對象,我們可以隨時主動調用歷史條目

此處引用 mozilla 的例子

<!DOCTYPE HTML>
<!-- this starts off as http://example.com/line?x=5 -->
<title>Line Game - 5</title>
<p>You are at coordinate <span id="coord">5</span> on the line.</p>
<p><a href="?x=6" onclick="go(1); return false;">Advance to 6</a> or<a href="?x=4" onclick="go(-1); return false;">retreat to 4</a>?
</p>
<script>var currentPage = 5; // prefilled by server!!!!function go(d) {setupPage(currentPage + d);history.pushState(currentPage, document.title, '?x=' + currentPage);}onpopstate = function(event) {setupPage(event.state);}function setupPage(page) {currentPage = page;document.title = 'Line Game - ' + currentPage;document.getElementById('coord').textContent = currentPage;document.links[0].href = '?x=' + (currentPage+1);document.links[0].textContent = 'Advance to ' + (currentPage+1);document.links[1].href = '?x=' + (currentPage-1);document.links[1].textContent = 'retreat to ' + (currentPage-1);}
</script>

我們點擊?Advance to ??對應的 url 與模版都會 +1,反之點擊?retreat to ??就會都 -1,這就滿足了 url 與模版視圖同時變化的需求

實際當中我們不需要去模擬 onpopstate 事件,官方文檔提供了 popstate 事件,當我們在歷史記錄中切換時就會產生 popstate 事件。對于觸發 popstate 事件的方式,各瀏覽器實現也有差異,我們可以根據不同瀏覽器做兼容處理。

hash

我們經常在 url 中看到 #,這個 # 有兩種情況,一個是我們所謂的錨點,比如典型的回到頂部按鈕原理、Github 上各個標題之間的跳轉等,路由里的 # 不叫錨點,我們稱之為 hash,大型框架的路由系統大多都是哈希實現的。

同樣我們需要一個根據監聽哈希變化觸發的事件 —— hashchange 事件

我們用?window.location?處理哈希的改變時不會重新渲染頁面,而是當作新頁面加到歷史記錄中,這樣我們跳轉頁面就可以在 hashchange 事件中注冊 ajax 從而改變頁面內容。

這里我在 codepen 上模擬了一下原理:?http://codepen.io/orangexc/pe...點擊預覽

hashchange 在低版本 IE 需要通過輪詢監聽 url 變化來實現,我們可以模擬如下

(function(window) {// 如果瀏覽器不支持原生實現的事件,則開始模擬,否則退出。if ( "onhashchange" in window.document.body ) { return; }var location = window.location,oldURL = location.href,oldHash = location.hash;// 每隔100ms檢查hash是否發生變化setInterval(function() {var newURL = location.href,newHash = location.hash;// hash發生變化且全局注冊有onhashchange方法(這個名字是為了和模擬的事件名保持統一);if ( newHash != oldHash && typeof window.onhashchange === "function"  ) {// 執行方法window.onhashchange({type: "hashchange",oldURL: oldURL,newURL: newURL});oldURL = newURL;oldHash = newHash;}}, 100);
})(window);

大型框架的路由當然不會這么簡單,angular 1.x 的路由對哈希、模版、處理器進行關聯,大致如下

app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {$routeProvider.when('/article', {templateUrl: '/article.html',controller: 'ArticleController'}).otherwise({redirectTo: '/index'});$locationProvider.html5Mode(true);
}])

這套路由方案默認是以 # 開頭的哈希方式,如果不考慮低版本瀏覽器,就可以直接調用?$locationProvider.html5Mode(true)?利用 H5 的方案而不用哈希方案。

總結

兩種方案我推薦 hash 方案,因為照顧到低級瀏覽器,就是不美觀(多了一個 #),兩者兼顧也不是不可,只能判斷瀏覽器給出對應方案啦,不過也只支持 IE8+,更低版本兼容見上文!

這個鏈接的 demo 含有判斷方法:http://sandbox.runjs.cn/show/...?。同時給出 Github 倉庫地址:?minrouter,推薦大家讀下源碼,僅僅 117 行,精辟!

如果在上面鏈接測試時你的 url 里多了一個 #,說明你的瀏覽器該更新啦。

文章出自 orange 的 個人博客?http://orangexc.xyz/

轉載于:https://my.oschina.net/ZhenyuanLiu/blog/1802350

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/538813.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/538813.shtml
英文地址,請注明出處:http://en.pswp.cn/news/538813.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2.JAVA簡史

SUN公司 --美國SUN&#xff08;Stanford university network&#xff09;公司 --在中國大陸的正式中文名&#xff1a;太陽計算機系統&#xff08;中國&#xff09;有限公司 --在中國臺灣中文名&#xff1a;升陽電腦公司 JAVA為什么被發明&#xff1f; --是sun公司Green項目…

es統計有多少個分組_ES 24 - 如何通過Elasticsearch進行聚合檢索 (分組統計)

1 普通聚合分析1.1 直接聚合統計(1) 計算每個tag下的文檔數量, 請求語法:GET book_shop/it_book/_search{"size": 0, // 不顯示命中(hits)的所有文檔信息"aggs": {"group_by_tags": {// 聚合結果的名稱, 需要自定義(復制時請去掉此注釋)"te…

python程序運行原理_談談 Python 程序的運行原理

因為我的個人網站 restran.net 已經啟用&#xff0c;博客園的內容已經不再更新。請訪問我的個人網站獲取這篇文章的最新內容&#xff0c;談談 Python 程序的運行原理 這篇文章準確說是『Python 源碼剖析』的讀書筆記&#xff0c;整理完之后才發現很長&#xff0c;那就將就看吧。…

3.JDK和JRE和JVM的區別

JDK --Java Development Kit --java 開發工具包 JRE --Java Runtime Environment --java運行時環境 JVM --Java Virtual Machine --java虛擬機 ------------- 更多的Java&#xff0c;Angular&#xff0c;Android&#xff0c;大數據&#xff0c;J2EE&#xff0c;Python…

緩存cache

由于Django是動態網站&#xff0c;所有每次請求均會去數據進行相應的操作&#xff0c;當程序訪問量大時&#xff0c;耗時必然會更加明顯&#xff0c;最簡單解決方式是使用&#xff1a;緩存&#xff0c;緩存將一個某個views的返回值保存至內存或者memcache中&#xff0c;5分鐘內…

微信小程序 等待幾秒、_微信小程序—setTimeout定時器的坑

背景實驗室需要將項目的app搬到微信的小程序上&#xff0c;終于知道為什么程序員是手藝人了&#xff0c;只要有需求&#xff0c;就要想方設法去填充這種需求&#xff0c;去年是小程序的元年了可以說&#xff0c;去年冬天一個叫跳一跳的小程序游戲出現在我的微信中&#xff0c;當…

linux中斷處理模式,Linux在保護模式下的中斷處理分析.pdf

Linux在保護模式下的中斷處理分析.pdfLinux 在保護模式下的中斷處理分析劉萬里 楊 斌(西南交通大學計算機與通信工程學院&#xff0c;成都 610031)E-mail&#xff1a;awan摘 要 該文以 80x86 保護模式下的中斷處理方法為基礎&#xff0c;針對 Linux 在實時嵌入式系統中的具體應…

python3.7是什么_Python 3.7 有什么新變化

idlelib 與 IDLE 多個對自動補全的修正。 &#xff08;由 Louie Lu 在 bpo-15786 中貢獻。&#xff09; Module Browser (在 File 菜單中&#xff0c;之前稱為 Class Browser) 現在會在最高層級函數和類之外顯示嵌套的函數和類。 &#xff08;由 Guilherme Polo, Cheryl Sabell…

4.JVM簡述

JVM是一種規范。 就是一個虛擬的用于執行bytecodes字節碼的計算機 可以用軟件來實現&#xff0c;如IBM,SUN,BEA等按照這個規范實現&#xff0c;可以實現比SUN公司更好的JVM&#xff0c;我們自己也可以實現一個。 可以使用硬件來實現&#xff0c;如sun與intel公司研發java的芯…

python ssh shell交互_使用Paramiko在Python上用ssh實現交互式shell?

我想編寫一個程序(在Windows 7上的Python 3.x中),它通過ssh在遠程shell上執行多個命令.在查看paramikos的exec_command()函數之后,我意識到它不適合我的用例(因為在執行命令后通道被關閉),因為命令依賴于環境變量(由先前的命令設置)并且不能連接到一個exec_command()調用,因為它…

linux7如何進入緊急模式,CentOS7開機進入緊急模式EmergencyMode的解決辦法

iOS Runtime學習筆記Associated Objects: interface NSObject (AssociatedObject) property (nonatomic, strong) id associat ...Vim&#xff0c;極簡使用教程&#xff0c;讓你瞬間脫離鍵鼠切換的痛苦注:看大家對Vim仇恨極大,其實它只是一種文本操作方式,可以減少鍵鼠的切換,從…

用pycharm寫python_如何利用pyCharm編寫和運行python文件

在安裝python環境后&#xff0c;通常可以利用IDE pyCharm來編譯我們的python文件。創建一個python文件夾&#xff0c;用pyCharm打開文件夾&#xff0c;在文件夾中新建一個python文件demo.py 也許你知道用cmd中的python指令 python demo.py去運行這個文件&#xff0c;但是如何在…

5.JDK環境配置

下載 進入Oracle官網下載&#xff0c;點擊進入 安裝 一路下一步。記住安裝到哪里了。 配置環境變量 JAVA_HOME 剛才的java安裝目錄 PATH %JAVA_HOME%\bin PATH里配置多個用英文的分號; 分隔。 *classpath&#xff0c;jdk5.0以上可以不用配置了 測試 windows下&#xf…

GBK 編碼

GBK編碼范圍&#xff1a;8140&#xff0d;FEFE&#xff0c;漢字編碼范圍見第二節&#xff1a;碼位分配及順序。 GBK編碼&#xff0c;是對GB2312編碼的擴展&#xff0c;因此完全兼容GB2312-80標準。GBK編碼依然采用雙字節編碼方案&#xff0c;其編碼范圍&#xff1a;8140&#x…

less webpack 熱更新_webpack---less+熱更新 使用

最近嘗試用less寫界面,webpack進行打包&#xff0c;然后發現每次修改less時都需要重新執行webpack打包一下&#xff0c;于是就想到了webpack熱更新這個功能。一、使用lessless是一門css預處理語言&#xff0c;它是拓展了css&#xff0c;增加了變量&#xff0c;Mixin等等。使用l…

6.第一個程序Hello World

新建文件夾 在C盤新建個文件夾 mycode。注意不要用中文。 新建java文件 1、顯示隱藏文件名。 2、右鍵新建文本文件 3、重命名為 Welcome.java。&#xff08;首字母必須大寫。如果不顯示隱藏文件名&#xff0c;會是Welcome.java.txt不是java文件&#xff09; 4、編寫代碼 p…

pythonstdin_python 筆試輸入:sys.stdin.readline和input

①&#xff1a;輸入一行數據并輸 出兩種方法 # 輸入一行數據并輸出 import sys # 方法一&#xff1a; str1 input() print(input 輸入:,str1,len,len(str1)) print(循環遍歷輸入得到輸入的每個字符的ascii碼如下&#xff1a;) for i in str1: print(ord(i)) # 方法二&#xff…

c語言字符串二維數組的動態分配應,C語言中動態分配二維數組復習過程.doc

C語言中動態分配二維數組復習過程.docC語言中動態分配二維數組在C中動態分配內存的&#xff0c;對于單個變量&#xff0c;字符串&#xff0c;一維數組等&#xff0c;都是很容易的。C中動態分配二維數組的方法&#xff0c;很少有C語言書中描述&#xff0c;我查找了有的C語言書中…

7.Java常用開發工具

文本編輯器 任意選一款 UltraEdit EditPlus notepad 集成開發環境 IDE&#xff1a;Integrated Development Environment --JBulider &#xff08;基本淘汰&#xff09; --Eclipse 普遍使用。https://www.eclipse.org/downloads/ --NetBeans ------------- 更多的Java&a…

python京東商品_Python爬取京東的商品分類與鏈接

前言本文主要的知識點是使用Python的BeautifulSoup進行多層的遍歷。如圖所示。只是一個簡單的哈&#xff0c;不是爬取里面的隱藏的東西。示例代碼from bs4 import BeautifulSoup as bsimport requestsheaders {"host": "www.jd.com","User-Agent&quo…