衆所周知,vue-router
有三種模式 :hash
、html5
、abstract
, 通常的前端項目中會選擇hash
模式進行開發,最近作了一個運營活動就是基於vue-router的hash模式
進行開發的。html
var router = new VueRouter({
routes: [{
name: 'index',
path: '',
component: indexcomponent
},{
name: 'demo',
path: '/demo',
component: democomponent
}]
});
複製代碼
入口頁面須要參數,因此提供URL:https://www.xxx.com?from=weixin
, 瀏覽器裏輸入URL回車後,頁面自動增長一個#/
變爲https://www.xxx.com?from=weixin#/
。前端
index頁面中一個按鈕點擊後跳轉demo,同時想攜帶index中獲取的參數,看API選擇了以下方式,結果URL變成了:https://www.xxx.com?from=weixin#/test?userId=123
vue
router.push({
path: 'demo',
query: {
plan: 'private'
}
})
複製代碼
統一資源定位符(或稱統一資源定位器/定位地址、URL地址等,英語:Uniform Resource Locator,常縮寫爲URL)html5
標準格式:scheme:[//authority]path[?query][#fragment]
git
例子github
下圖展現了兩個 URI 例子及它們的組成部分。vue-router
hierarchical part ┌───────────────────┴─────────────────────┐ authority path ┌───────────────┴───────────────┐┌───┴────┐ abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1 └┬┘ └───────┬───────┘ └────┬────┘ └┬┘ └─────────┬─────────┘ └──┬──┘ scheme user information host port query fragment urn:example:mammal:monotreme:echidna └┬┘ └──────────────┬───────────────┘ scheme path
『?』segmentfault
『#』設計模式
Request Headers
中的Referer
不包含#http://www.xxx.com/?color=#fff
發出請求是:/color=
)window.location.hash
讀取#值URL讀取和操做涉及location和history兩個對象,具體以下:api
location API :
history API:
初始化router的時候,根據指定的mode選擇路由實現,固然mode判斷有必定邏輯和兼容策略
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}`)
}
}
複製代碼
咱們選擇hash
模式進行深刻分析,對應HashHistory
模塊,該模塊是history/hash.js
實現的,當被調用的時候,對全局路由變化進行了監聽
window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => {
...
})
複製代碼
同時hash.js
中也實現了push
等api方法的封裝,咱們以push
爲例,根據源碼能夠看出,它的實現是基於基類transitionTo
的實現,具體以下:
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
pushHash(route.fullPath)
handleScroll(this.router, route, fromRoute, false)
onComplete && onComplete(route)
}, onAbort)
}
複製代碼
既然調用了transitionTo
那麼來看它的實現,獲取參數後調用confirmTransition
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
// 獲取URL中的參數
const route = this.router.match(location, this.current)
this.confirmTransition(route, () => {
this.updateRoute(route)
onComplete && onComplete(route)
this.ensureURL()
...
})
}
複製代碼
同時confirmTransition
裏實現了一個隊列,順序執行,iterator
經過後執行next
,進而志新pushHash()
,實現頁面hash改變,最終實現了${base}#${path}
的鏈接
function getUrl (path) {
const href = window.location.href
const i = href.indexOf('#')
const base = i >= 0 ? href.slice(0, i) : href
return `${base}#${path}`
}
function pushHash (path) {
if (supportsPushState) {
pushState(getUrl(path))
} else {
window.location.hash = path
}
}
複製代碼
https://www.xxx.com?from=weixin#/test?userId=123
這個頁面看起來感受怪,是由於這個鏈接中幾乎包含了全部的參數,並且hash裏面還有一個問號,一個URL中多個問號的不常見