window.history(可直接寫成history)指向History對象,它表示當前窗口的瀏覽歷史。History對象保存了當前窗口訪問過的全部頁面網址css
history對象的常見屬性和方法
go()
接受一個整數爲參數,移動到該整數指定的頁面,好比history.go(1)至關於history.forward(),history.go(-1)至關於history.back(),history.go(0)至關於刷新當前頁面
back()
移動到上一個訪問頁面,等同於瀏覽器的後退鍵,常見的返回上一頁就能夠用back(),是從瀏覽器緩存中加載,而不是從新要求服務器發送新的網頁
forward()
移動到下一個訪問頁面,等同於瀏覽器的前進鍵
pushState()
在瀏覽器歷史中添加記錄,方法接受三個參數,以此爲:html
history.pushstate(state,title,url) if(!!(window.hostory && history.pushState)) { // 支持History API } else { // 不支持 }
state: 一個與指定網址相關的狀態對象,popState事件觸發時,該對象會傳入回調函數,若是不須要這個對象,此處可填null
title: 新頁面的標題,可是全部瀏覽器目前都忽略這個值,所以這裏能夠填null
url: 新的網址,必須與當前頁面處在同一個域,瀏覽器的地址欄將顯示這個網址vue
history.pushState({a:1},'page 2','2.html')
用上面代碼添加2.html後,瀏覽器地址欄馬上顯示2.html,但不會跳到2.html,只會更新瀏覽器歷史記錄,此時點擊後退按鈕則會回到原網頁,可是會改變history的length屬性;
若是pushState的url參數,設置了一個新的錨點值(即hash),並不會觸發hashChange事件,若是設置了一個跨域網址,則會報錯。vue-router
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中,它提供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 我在桌上刻個早字 謝謝啦