HTML5History
class HTML5History extends History{
_startLocation;
constructor(router,base){
super(router,base)
this._startLocation = getLocation(this.base)
}
setupListeners(){
if(this.listeners.lenth > 0){
return
}
const router = this.router
const expectScroll = router.options.scrollBehavior
const supportsScroll = supportsPushState && expectScroll
if(supportScroll){
this.listeners.push(setupScroll())
}
const handleRoutingEvent = () => {
const current = this.current
const location = getLocation(this.base)
if(this.current === START && location === this._startLocation){
return
}
this.transitionTo(location,route => {
if(supportScroll){
handleScroll(router,route,current,true)
}
})
}
window.addEventListener('popstate',handleRoutingEVent)
this.listeners.push(() => {
window.removeEventListener('popstate',handleRoutingEvent)
})
}
go(n){
window.history.go(n)
}
push(location,onComplete,onAbort){
const { current: fromRoute } = this
this.transitionTo(location,route => {
pushState(cleanPath(this.base+ route.fullPath))
handleScroll(this.router,route,fromRoute,false)
onComplete && onComplete(route)
},onAbort)
}
replace(location,onComplete,onAbort){
const { current: fromRoute } = this
this.transitionTo(location,route => {
replaceState(cleanPath(this.base + route.fullPath))
handleScroll(this.router,route,fromRoute,false)
onComplete && onComplete(route)
},onAbort)
}
ensureURL(push){
if(getLocation(this.base) !== this.current.fullPath){
const current = cleanPath(this.base+ this.current.fullPath)
push ? pushState(current) : replaceState(current)
}
}
getCurrentLocation(){
return getLocation(this.base)
}
}
setupScroll
function setupScroll(){
if('scrollRestoration' in window.history){
window.history.scrollRestoration = 'manual'
}
const protocolAndPath = window.location.protocol + '//' + window.location.host
const absolutePath = window.location.href.replace(protocolAndPath,'')
const stateCopy = extend({},window.history.state)
stateCopy.key = getStateKey()
window.history.replaceState(stateCopy,'',absolutePath)
window.addEvenetListener('popstate',handlePopState)
return () => {
window.removeEventListener('popstate',handlePopState)
}
}
getStateKey
const Time = inBrowser && window.performance && window.performance.now ? window.performance : Date
const genStateKey = () => Time.now().toFixed(3)
let _key = genStateKey()
const getStateKey = () => _key
const setStateKey = (key) => _key = key
handlePopState
function handlePopState(e){
saveScrollPosition()
if(e.state && e.state.key){
setStateKey(e.state.key)
}
}
const positionStore = Object.create(null)
function saveScrollPosition(){
const key = getStateKey()
if(key){
positionStore[key] = {x:window.pageXOffset,y:window.pageYOffset}
}
}
handleScroll
function handleScroll(router,to,from,isProp){
if(!router.app){
return
}
const behavior = router.options.scrollBehavior
if(!behavior){
return
}
...
router.app.$nextTick(() => {
const position = getScrollPosition()
const shouldScroll = behavior.call(router,to,from,isProp ? position : null)
if(!shouldScroll) return
if(typeof shouldScroll.then === 'function'){
shouldScroll.then(shouldScroll => {
scrollPosition(shouldScroll,position)
}).catch(err => {
if(process.env.NODE_ENV !== 'production'){
assert(false,err.toString)
}
})
}else{
scrollPosition(shouldScroll,position)
}
})
}
scrollPosition
function scrollPosition(shouldScroll,position){
const isObject = typeof shouldScroll === 'object'
if(isObject && typeof shouldScroll.selector === 'string'){
const el = hashStartsWithNumberRE.test(shouldScroll.selector) ? document.getElementById(shouldScroll.selector.slice(1)) : document.querySelector(shouldScroll.selector)
if(el){
let offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}
offset = normalizeOffset(offset)
position = getElementPosition(el,offset)
}else if(isValidPosition(shouldScroll)){
position = normalizePosition(shouldScroll)
}
}else if(isObject && isValidPosition(shouldScroll)){
position = normalizePosition(shouldScroll)
}
if(position){
if('scrollBehavior' in document.documentElement.style){
window.scrollTo({
left: position.x,
top: position.y,
behavior: shouldScroll.behavior
})
}else{
window.scrollTo(position.x,position.y)
}
}
}
getLocation
function getLocation(base){
let path = window.location.pathname
if(base && path.toLowerCase().indexOf(base.toLowerCase()) === 0){
path = path.slice(base.length)
}
return (path || '/') + window.location.search + window.location.hash
}
supportPushState
const supportPushStat = inBrowser && (function(){
const ua = window.navigator.userAgent
if( (ua.indexOf('Android 2.')!== -1 || ua.indexOf('Android 4.0') !== -1)&& ua.indexOf('Mobile Safari') !== -1 && ua.indexOf('chrome') === -1 && ua.indexOf('WindowsPhone') ===) return false
return window.history && typeof window.history.pushState === 'function'
})()
pushState
function pushState(url,replace){
saveScrollPosition()
const history = window.history
try{
if(replace){
const stateCopy = extend({},history.state)
stateCopy.key = getStateKey()
histroy.replaceState(stateCopy,'',url)
}else{
history.pushState({key:setStateKey(getStateKey())},'',url)
}
}catch(e){
window.location[replace ? 'replace' : 'assign'](url)
}
}