摘要:另外該事件只針對同一個文檔,如果瀏覽歷史的切換,導致加載不同的文檔,該事件不會被觸發使用的時候,可以為事件指定回調函數或者回調函數的參數是一個事件對象,它的屬性指向和方法為當前所提供的狀態對象即這兩個方法的第一個參數。
history
window.history(可直接寫成history)指向History對象,它表示當前窗口的瀏覽歷史。History對象保存了當前窗口訪問過的所有頁面網址
history對象的常見屬性和方法
go()
接受一個整數為參數,移動到該整數指定的頁面,比如history.go(1)相當于history.forward(),history.go(-1)相當于history.back(),history.go(0)相當于刷新當前頁面
back()
移動到上一個訪問頁面,等同于瀏覽器的后退鍵,常見的返回上一頁就可以用back(),是從瀏覽器緩存中加載,而不是重新要求服務器發送新的網頁
forward()
移動到下一個訪問頁面,等同于瀏覽器的前進鍵
pushState()
在瀏覽器歷史中添加記錄,方法接受三個參數,以此為:
history.pushstate(state,title,url) if(!!(window.hostory && history.pushState)) { // 支持History API } else { // 不支持 }
state: 一個與指定網址相關的狀態對象,popState事件觸發時,該對象會傳入回調函數,如果不需要這個對象,此處可填null
title: 新頁面的標題,但是所有瀏覽器目前都忽略這個值,因此這里可以填null
url: 新的網址,必須與當前頁面處在同一個域,瀏覽器的地址欄將顯示這個網址
history.pushState({a:1},"page 2","2.html")
用上面代碼添加2.html后,瀏覽器地址欄立刻顯示2.html,但不會跳到2.html,只會更新瀏覽器歷史記錄,此時點擊后退按鈕則會回到原網頁,但是會改變history的length屬性;
如果pushState的url參數,設置了一個新的錨點值(即hash),并不會觸發hashChange事件,如果設置了一個跨域網址,則會報錯。
replaceState()
history.replaceState()方法的參數和pushState()方法一摸一樣,區別是它修改瀏覽器歷史當中的記錄
兩者的區別在于
push
此時執行history.back()返回/about
replace
此時執行history.back()返回/blog
length
history.length屬性保存著歷史記錄的url數量,初始時該值為1,如果當前窗口先后訪問了三個網址,那么history對象就包括3項,history.length=3
state
返回當前頁面的state對象。可以通過replaceState()和pushState()改變state,可以存儲很多數據
scrollRestoration
history.scrollRestoration = "manual";關閉瀏覽器自動滾動行為
history.scrollRestoration = "auto";打開瀏覽器自動滾動行為(默認)
popState 事件
每當同一個文檔的瀏覽歷史(即history)出現變化時,就會觸發popState事件
需要注意:僅僅調用pushState方法或replaceState方法,并不會觸發該事件,只有用戶點擊瀏覽器后退和前進按鈕時,或者使用js調用back、forward、go方法時才會觸發。另外該事件只針對同一個文檔,如果瀏覽歷史的切換,導致加載不同的文檔,該事件不會被觸發
使用的時候,可以為popState事件指定回調函數
window.onpopstate = function (event) { console.log("location: " + document.location); console.log("state: " +JSON.stringify(event.state)); }; // 或者 window.addEventListener("popstate", function(event) { console.log("location: " + document.location); console.log("state: " + JSON.stringify(event.state)); });
回調函數的參數是一個event事件對象,它的state屬性指向pushState和replaceState方法為當前url所提供的狀態對象(即這兩個方法的第一個參數)。上邊代碼中的event.state就是通過pushState和replaceState方法為當前url綁定的state對象
這個state也可以直接通過history對象讀取
history.state
注意:頁面第一次加載的時候,瀏覽器不會觸發popState事件
hash 就是指 url 尾巴后的 # 號以及后面的字符。這里的 # 和 css 里的 # 是一個意思。hash 也 稱作 錨點,本身是用來做頁面定位的,她可以使對應 id 的元素顯示在可視區域內。
通過window.location.hash獲取hash值
延伸:
window.location對象里面
hash : 設置或返回從 (#) 開始的 URL(錨)。
host : 設置或返回主機名和當前 URL 的端口號。
hostname:設置或返回當前 URL 的主機名。
href : 設置或返回完整的 URL。
pathname: 設置或返回當前 URL 的路徑部分。
port:設置或返回當前 URL 的端口號。
search : 設置或返回從問號 (?) 開始的 URL(查詢部分)。
assign() : 加載新的文檔。
reload() : 重新加載當前文檔。
replace() : 用新的文檔替換當前文檔。
hashchange
當hash值改變時會觸發這個事件,
if("onhashchange" in window) { window.addEventListener("hashchange",function(e){ console.log(e.newURL,e.oldURL) },false) }vue-router
在vue-router中,它提供mode參數來決定采用哪一種方式;
默認是hash,可以配置mode:history,選擇history模式;
選好mode后 vueRouter中會創建history對象(HashHistory或HTML5History,這兩種類都是繼承History類,這個類定義了一些公共方法)
// 根據mode確定history實際的類并實例化 switch (mode) { case "history": this.history = new HTML5History(this, options.base) break case "hash": this.history = new HashHistory(this, options.base, this.fallback) break case "abstract": this.history = new AbstractHistory(this, options.base) break default: if (process.env.NODE_ENV !== "production") { assert(false, `invalid mode: ${mode}`) } } }
現在我們來看當我們在代碼中執行了this.$router.push()之后具體的流程
首先看HashHistory
1 $router.push() //顯式調用方法
2 HashHistory.push() // 我們來看下push方法
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.transitionTo(location, route => { pushHash(route.fullPath) onComplete && onComplete(route) }, onAbort) } function pushHash (path) { window.location.hash = path }
transitionTo()方法是父類中定義的是用來處理路由變化中的基礎邏輯的,push()方法最主要的是對window的hash進行了直接賦值:hash的改變會自動添加到瀏覽器的訪問歷史記錄中。
window.location.hash = route.fullPath //類似/thunder/bless_sort/1?fromType=homeTap
那么視圖的更新是怎么實現的呢,我們來看父類History中transitionTo()方法的這么一段:
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) { const route = this.router.match(location, this.current) this.confirmTransition(route, () => { this.updateRoute(route) ... }) } updateRoute (route: Route) { this.cb && this.cb(route) } listen (cb: Function) { this.cb = cb }
路由變化后會執行updateRoute(),其實是執行this.cb ,而 this.cb是在listen函數中被執行的,那么在那里調用listen函數呢
init (app: any /* Vue component instance */) { this.apps.push(app) history.listen(route => { this.apps.forEach((app) => { app._route = route }) }) }
app為vue組件實例,vue本身是沒有vue-routerd的,需要在組件中掛載這個屬性
export function install (Vue) { Vue.mixin({ beforeCreate () { if (isDef(this.$options.router)) { this._router = this.$options.router this._router.init(this) Vue.util.defineReactive(this, "_route", this._router.history.current) } registerInstance(this, this) }, }) }
通過Vue.mixin()方法,全局注冊一個混合,影響注冊之后所有創建的每個 Vue 實例,該混合在beforeCreate鉤子中通過Vue.util.defineReactive()定義了響應式的_route屬性(當前的路由)。即當_route值改變時,會自動調用Vue實例的render()方法,更新視圖。
總結一下,從設置路由改變到視圖更新的流程如下:
$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()
replace方法
功能: 替換當前路由并更新視圖,常用情況是地址欄直接輸入新地址
流程與push基本一致
但流程2變為替換當前hash (window.location.replace= XXX)
replace和hash的區別在于它并不是將新路由添加到瀏覽器訪問歷史的棧頂,而是替換掉當前的路由:如上圖
監聽地址欄
以上討論的VueRouter.push()和VueRouter.replace()是可以在vue組件的邏輯代碼中直接調用的,除此之外在瀏覽器中,用戶還可以直接在瀏覽器地址欄中輸入改變路由,因此VueRouter還需要能監聽瀏覽器地址欄中路由的變化,并具有與通過代碼調用相同的響應行為。在HashHistory中這一功能通過setupListeners實現:
setupListeners () { window.addEventListener("hashchange", () => { if (!ensureSlash()) { return } this.transitionTo(getHash(), route => { replaceHash(route.fullPath) }) }) }
該方法設置監聽了瀏覽器事件hashchange,調用的函數為replaceHash,即在瀏覽器地址欄中直接輸入路由相當于代碼調用了replace()方法
而在HTML5History具體又是怎樣的呢
代碼結構以及更新視圖的邏輯與hash模式基本類似,只不過將對window.location.hash直接進行賦值window.location.replace()改為了調用history.pushState()和history.replaceState()方法。
在HTML5History中添加對修改瀏覽器地址欄URL的監聽是直接在構造函數中執行的:監聽popState事件(地址欄變化觸發window.onpopstate),調用repalce方法
constructor (router: Router, base: ?string) { window.addEventListener("popstate", e => { const current = this.current this.transitionTo(getLocation(this.base), route => { if (expectScroll) { handleScroll(router, route, current, true) } }) }) }
除此之外vue-router還為非瀏覽器環境準備了一個abstract模式,其原理為用一個數組stack模擬出瀏覽器歷史記錄棧的功能。以上是vue-router的核心邏輯;
兩種模式對比
History模式的優點:
1.History模式的地址欄更美觀。。。
2.History模式的pushState、replaceState參數中的新URL可為同源的任意URL(可為不同的html文件),而hash只能是同一文檔
3.History模式的pushState、replaceState參數中的state可為js對象,能攜帶更多數據
4.History模式的pushState、replaceState參數中的title能攜帶字符串數據(當然,部分瀏覽器,例如firefox不支持title,一般title設為null,不建議使用)
缺點:
不過這種模式需要后端配置,因為我們這個頁面是單頁面應用,如果用戶直接訪問http://oursite.com/user/id
后臺沒有正確的配置,則就會返回404,
這個時候需要后臺配置一個能夠覆蓋所有情況的候選資源,如果url匹配不到任何靜態資源時,則要返回同一個index.html;
注:該篇文章參考了https://zhuanlan.zhihu.com/p/...
轉載請注明作者 : crystal 我在桌上刻個早字 謝謝啦
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/104709.html
摘要:我們知道是的核心插件,而當前項目一般都是單頁面應用,也就是說是應用在單頁面應用中的。原理是傳統的頁面應用,是用一些超鏈接來實現頁面切換和跳轉的其實剛才單頁面應用跳轉原理即實現原理實現原理原理核心就是更新視圖但不重新請求頁面。 近期面試,遇到關于vue-router實現原理的問題,在查閱了相關資料后,根據自己理解,來記錄下。我們知道vue-router是vue的核心插件,而當前vue項目...
摘要:源碼解讀閱讀請關注下代碼注釋打個廣告哪位大佬教我下怎么排版啊,不會弄菜單二級導航撲通是什么首先,你會從源碼里面引入,然后再傳入參數實例化一個路由對象源碼基礎類源碼不選擇模式會默認使用模式非瀏覽器環境默認環境根據參數選擇三種模式的一種根據版 router源碼解讀 閱讀請關注下代碼注釋 打個廣告:哪位大佬教我下sf怎么排版啊,不會弄菜單二級導航(撲通.gif) showImg(https:...
摘要:源碼解讀閱讀請關注下代碼注釋打個廣告哪位大佬教我下怎么排版啊,不會弄菜單二級導航撲通是什么首先,你會從源碼里面引入,然后再傳入參數實例化一個路由對象源碼基礎類源碼不選擇模式會默認使用模式非瀏覽器環境默認環境根據參數選擇三種模式的一種根據版 router源碼解讀 閱讀請關注下代碼注釋 打個廣告:哪位大佬教我下sf怎么排版啊,不會弄菜單二級導航(撲通.gif) showImg(https:...
閱讀 3312·2021-09-02 15:41
閱讀 2840·2021-09-02 09:48
閱讀 1382·2019-08-29 13:27
閱讀 1170·2019-08-26 13:37
閱讀 846·2019-08-26 11:56
閱讀 2491·2019-08-26 10:24
閱讀 1652·2019-08-23 18:07
閱讀 2626·2019-08-23 15:16