With business functions together more complex front-end applications, the user experience for the increasingly high demand, single-sided ( SPA
) become the mainstream in the form of front-end applications. One of the most notable feature is the large single-page application distal routing system employed, by changing URL
, without re-page request, updates the page view.
Update view but not re-requested page is one of the core principles of the front-end route, currently in the browser environment to achieve this main function of 2
ways:
- Using
URL
inhash
("#"
); - Use
History interface
inHTML5
the new method;
vue-router
A Vue.js
routing plug-in framework, which is by mode
mode of realization of this parameter control route:
const router=new VueRouter({ mode:'history', routes:[...] })
Created VueRouter
when an instance of the object mode
passed in the form of configuration parameters.
src/index.js
export default class VueRouter{ the MODE: String; // passed in a string parameter indicating the history category history: HashHistory | HTML5History | AbstractHistory; // actual functioning of object properties, must be at least three classes of enumeration fallback: boolean ; // such as browsing does not support, 'history' mode need to rollback 'hash' model constructor (Options: RouterOptions = {}) { the MODE the let = options.mode || 'hash' // default to 'hash' mode the this .fallback the MODE = === 'history' &&! supportsPushState // by supportsPushState determine whether the browser supports the 'history' mode IF ( the this . fallback) { mode = 'hash' } if (!inBrowser) { MODE = 'abstract' // the browser environment is not forced to run for an 'abstract' mode } the this .mode = MODE // 根据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}`) } } } init (app: any /* Vue component instance */) { const history = this.history // perform a corresponding initialization operation history according to the type and monitor IF (history the instanceof HTML5History) { history.transitionTo(history.getCurrentLocation()) } else if (history instanceof HashHistory) { const setupHashListener = () => { history.setupListeners() } history.transitionTo( history.getCurrentLocation(), setupHashListener, setupHashListener ) } history.listen(route => { this.apps.forEach((app) => { app._route = route }) }) } // the following method VueRouter class exposes the actual history is to call a specific object methods the Push (LOCATION: RawLocation, onComplete ?: Function, OnAbort? : Function) { the this .history.push (LOCATION, onComplete, OnAbort) } replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.history.replace(location, onComplete, onAbort) } }
- As a parameter string property
mode
is only a flag to indicate the actual function of object propertieshistory
implementation class, both of a correspondence relationship:
modehistory:
'history':HTML5History;
'hash':HashHistory; 'abstract':AbstractHistory;
- In the corresponding initialization
history
before, we will havemode
to do some checking: If the browser does not supportHTML5History
mode (bysupportsPushState
determining variable) ismode
sethash
; if it is not running in the browser environment, ismode
setabstract
; VueRouter
ClassonReady()
,push()
and so only a proxy method involves the specific callhistory
corresponding to the method of the object ininit()
the initialization process, also in accordancehistory
perform different operation target specific categories
HashHistory
hash
( "#"
) Have been added to the action symbol is URL
indicative of the position of the page:
http://www.example.com/index.html#print
#
Itself and the characters following it is called hash
by window.location.hash
the attribute reading.
hash
Although it appeared inurl
, but will not be included in thehttp
request, which is used to guide the action of the browser completely useless on the server side, therefore, changehash
will not reload the page.- You can be
hash
changed to add the event to listen:
window.addEventListener("hashchange",funcRef,false)
- Every change
hash
(window.location.hash
), it will increase access to a record in the browser history.
Use hash
of the above characteristics, since you can achieve front-end routing "update view but not re-requested page" function of.
HashHistory.push()
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.transitionTo(location, route => { pushHash(route.fullPath) onComplete && onComplete(route) } Onaboart) } function pushHash (path) { window.location.hash = path }
transitionTo()
The method is defined in the parent class is used to process the underlying logic of the routing changes, push()
the method is most important window
to hash
perform a direct assignment:
window.location.hash=route.fullPath
hash
The change will be automatically added to access the browser's history.
Then update the view is how to achieve it, we take a look at the parent class History
of transitionTo()
methods:
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 }
It can be seen when the route changes, call Hitory
the this.cb
method, and the this.cb
method is to History.listen(cb)
be set back to the VueRouter
class definition found in init()
were setting them:
init (app: any /* Vue component instance */) { this.apps.push(app) history.listen(route => { this.apps.forEach((app) => { app._route = route }) }) }
app
Is a Vue
component instance, but Vue
as a progressive front-end frame, the definition of component itself should be no built-in properties related to routing _route
, if the component to have this property, where the plug should be loaded, i.e., VueRouter
the install()
method of mixing Vue
the object, install.js
the Source:
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) }, }) }
ByVue.mixin()
the method, a mixed global registration, after registration of all the influence of each createdVue
instance, the mixingbeforeCreate
hook byVue.util.defineReactive()
the definition of the responsive_route
property. The so-called responsive properties, i.e., when_route
the value changes will automatically callVue
instancerender()
method, updating the view.
$router.push()-->HashHistory.push()-->History.transitionTo()-->History.updateRoute()-->{app._route=route}-->vm.render()
HashHistory.replace()
replace()
The method and push()
the difference method is that it does not add a new access route to the browser history stack, but replace the current route:
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.transitionTo(location, route => { replaceHash(route.fullPath) onComplete && onComplete(route) } Onaboart) } function replaceHash (path) { const i = window.location.href.indexOf('#') window.location.replace( window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path ) }
It can be seen that with push()
the implementation structure is substantially similar, it is not directly on the different window.location.hash
assignment, it calls the window.location.replace
method alternative routes.
Listen Address bar
The above VueRouter.push()
and VueRouter.replace()
is in the vue
call directly to the logic code assembly, except in the browser, the user can also change the routing input directly in the browser address bar, it is also necessary to listen route changes browser address bar and has the code to call through the same response behavior, HashHistory
in this by setupListeners
listening hashchange
realization:
setupListeners () { window.addEventListener('hashchange', () => { if (!ensureSlash()) { return } this.transitionTo(getHash(), route => { replaceHash(route.fullPath) }) }) }
The method set browser event listener hashchange
, call the function replaceHash
, the input routing is equivalent in the browser address bar code calls direct replace()
methods.
HTML5History
History interface
The interface is browser history stack provided by back()
, forward()
, go()
and other methods, we can read information stack browser history, various jump operations.
From the HTML5
beginning, History interface
we offer two new methods: pushState()
, replaceState()
allows us to modify the browser history stack:
window.history.pushState(stateObject,title,url)
window.history,replaceState(stateObject,title,url)
stateObject
: When the browser to jump to a new state, will trigger thepopState
event, which will carry thestateObject
copy of the parameterstitle
: The added record titleurl
: The added recordurl
This is 2
a method to have a common characteristic: When you call them to modify the browser history stack, although the current url
changed, but the browser does not immediately send the request url
, which is a single-page application front-end routing update the view but not re-requested page It provides the basis.
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { const { current: fromRoute } = this this.transitionTo(location, route => { pushState(cleanPath(this.base + route.fullPath)) handleScroll(this.router, route, fromRoute, false) onComplete && onComplete(route) } Onaboart) } replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { const { current: fromRoute } = this this.transitionTo(location, route => { replaceState(cleanPath(this.base + route.fullPath)) handleScroll(this.router, route, fromRoute, false) onComplete && onComplete(route) } Onaboart) } // src/util/push-state.js export function pushState (url?: string, replace?: boolean) { saveScrollPosition () // try...catch the pushState call to get around Safari // DOM Exception 18 where it limits to 100 pushState calls const history = window.history try { if (replace) { history.replaceState({ key: _key }, '', url) } else { _key = genKey() history.pushState({ key: _key }, '', url) } } catch (e) { window.location[replace ? 'replace' : 'assign'](url) } } export function replaceState (url?: string) { pushState(url, true) }
Logic and code structure as well as an updated view of hash
the basic model similar, but will window.location.hash()
direct assignment window.location.replace()
changed to call history.pushState()
and history.replaceState()
methods.
In HTML5History
adding to modify the browser address bar URL
listening popstate
is performed directly in the constructor:
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) } }) }) }
HTML5History
Use the HTML5
new features, you need to release Liu support, through supportsPushState
to check:
src/util/push-state.js
export const supportsPushState = 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('Windows Phone') === -1 ) { return false } return window.history && 'pushState' in window.history })()
These are the hash
mode and history
source mode REVIEW, this 2
kind of mode is through the browser interface, in addition, vue-router
also for non-browser environment to prepare a abstract
model, the principle for the use of an array of stack
simulated browser history stack Features.
Comparison of two models
General demand scenario, the hash
model and history
model is about the same, according to MDN
the introduction, calls history.pushState()
compared to directly modify hash
mainly has the following advantages:
pushState
The new seturl
may be the currenturl
of any homologousurl
, buthash
can be changed only#
the back part, it may be provided only with the current documenturl
pushState
The new settingsurl
can currentlyurl
exactly the same, we will also add the record to the stack, andhash
set the new value must be added to the original not the same as to trigger the recording to the stackpushState
BystateObject
You can add any type of data records, buthash
only a short string addedpushState
May be additionally providedtitle
attributes for subsequent use
history
Problem mode
For single page applications, the ideal scenario is to use when entering the application load only index.html
, subsequent network operations through the ajax
completion, not based on url
re-request the page, but if the user enter directly in the address bar and press Enter to restart the browser reload and other special circumstances.
hash
Mode change only the hash
content portion, and the hash
part is not included in the http
request ( hash
tape #
):
http://oursite.com/#/user/id //如请求,只会发送http://oursite.com/
So hash
meet according to the following pattern url
will not have a problem requests a page
The history
mode of the url
modification on the rear end of the normal request and url
as ( history
without #
)
http://oursite.com/user/id
If such a request to send, then the rear end, the rear end is not configured with /user/id
the get
routing process will return 404
an error.
The official recommended solution is to increase the service side a candidate resources to cover all situations: if URL
not match any static resources, you should return the same index.html
page that's what you app
rely pages. At the same time after doing so, the server no longer returns 404
an error page, because the return path will be for all index.html
files. To avoid this, in Vue
the application which covers all cases of routes, and then a given 404
page. Alternatively, if it is used Node.js
as a background, the route can be used to match the server URL
, when there is no matched return time 404
to achieve fallback
.