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