The paper
The advantages and disadvantages of a single page are not covered in this article, only the principle. There are two ways to implement single-page functionality:
- Use the anchor point hashHistory
- Take advantage of the browser’s browserHistory principle
HashHistory principle
Add hashchange listening
window.addEventListener(
'hashchange'.function() {
// Change the page when the hash changes})Copy the code
Change the hash
window.location.hash = 'aaa';
Copy the code
BrowserHistory principle
Add popState listener to listen to browser forward and backward. However, you cannot listen to pushState and replaceState. Therefore, you need to replace the page when pushState and replaceState are executed.
window.addEventListener(
'popstate'.function() {
// Change the page when the URL changes})Copy the code
To change urls, see MDN for pushState and replaceState documentation.
history.pushState({}, ' ', path);
history.replaceState({}, ' ', path);
Copy the code
Vue – the practice of the Router
The Router class collects data to determine the route type
- Mode: determines the routing mode. Instantiate HTML5History or HashHistory.
- Routes: Collects all routing information.
class Router {
constructor(options) {
this.options = options;
let mode = options.mode || 'hash';
this.routes = options.routes;
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
default:
/ / an error}}... }Copy the code
Second, the HTML5History
- The setupListeners method listens on popState to get the current path
- The push method changes the route
class HTML5History {
constructor (route, base) {
this.current = ' ';
}
setupListeners() {
const handleRoutingEvent = () = > {
this.current = location.pathname? .slice(1) | |'/';
}
window.addEventListener(
'popstate',
handleRoutingEvent
)
}
push (path) {
history.pushState({}, ' ', path); }}Copy the code
Third, HashHistory
- The setupListeners method listens on the Hashchange to get the current path
- The push method changes the route
class HashHistory {
constructor (route, base) {
this.current = ' ';
}
setupListeners() {
const handleRoutingEvent = () = > {
this.current = location.hash? .slice(1) | |'/';
}
window.addEventListener(
'hashchange',
handleRoutingEvent
)
}
push (location) {
window.location.hash = location
}
}
Copy the code
4. Trigger setupListeners
The Router will have an init initialization to perform listening; The push method performs route modification.
class Router {
init() {
this.history.setupListeners()
}
push (location) {
this.history.push(location)
}
}
Copy the code
To protect the reading experience, disconnect, explain how vue uses the plugin, and then go back to vue-Router
Using Router (see main.js)
The router appears in app.$options
const app = new Vue({
el: '#app',
router,
store,
render: h= > h(App)
})
console.log(app)
Copy the code
Understand the Vue. Use
Vue. Use (Router) {install (Router) {install (Router) {Vue (Router) {install (Router)}}
import Router from '@/utils/router'
Vue.use(Router)
Copy the code
Static method install
- In the beforeCreate phase before component instantiation, assign the instance value to this._routerRoot and execute init to trigger step 4 above.
- To set up hijacking, access this.$router and return this. _routerroot. _router
- Added global component router-view to render routing components
Router.install = function(Vue) {
Vue.mixin({
beforeCreate () {
if (this.$options.router) {
this._routerRoot = this
this._router = this.$options.router
this._router.init(this)}else {
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this}}})Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
Vue.component('router-view', {render(h){
return h(com) // com is content component}})}Copy the code
Now we have set the route listener and registered the router-view component, so how to render the corresponding component near the router-view component when the route changes?
6. Router-view content switching
- Declare a _Vue in the external scope and assign Vue to _Vue when install executes.
let _Vue;
Router.install = function(Vue) {
_Vue = Vue;
}
Copy the code
- HashHistory, HTML5History creates instances using _Vue
class HashHistory {
constructor (route, base) {
this.current = ' ';
this.app = new _Vue({
data() {
return {
path: '/'}},})}}class HTML5History {
constructor (route, base) {
this.current = ' ';
this.app = new _Vue({
data() {
return {
path: '/'}},})}}Copy the code
- The router-view component uses path in the instance created in Step 2 to filter all routing information and find corresponding components. Because the router-view component uses app.path, it adds a dependency. When app.path changes, the router-view component reexecutes.
Router.install = function(Vue) {... Vue.component('router-view', {render(h){
const path = this._routerRoot._router.history.app.path;
const routes = this._routerRoot._router.routes;
const route = routes.find((i) = > i.path === ` /${path}`)
const com = route ? route.component : routes.find((i) = > i.path === ` ` / 404).component
return h(com)
}
})
}
Copy the code
- Listen for route changes and change this.app.path to reroute the router-view component. PushState does not trigger popState listening, so change this.app.path separately
class HTML5History {...setupListeners() {
const handleRoutingEvent = () = > {
this.current = location.pathname? .slice(1) | |'/';
this.app.path = this.current;
}
window.addEventListener(
'popstate',
handleRoutingEvent
)
}
push (path) {
history.pushState({}, ' ', path);
this.app.path = path; }}class HashHistory {...setupListeners() {
const handleRoutingEvent = () = > {
this.current = location.hash? .slice(1) | |'/';
this.app.path = this.current;
}
window.addEventListener(
'hashchange',
handleRoutingEvent
)
}
push (location) {
window.location.hash = location
}
}
Copy the code
The complete code
let _Vue;
class HTML5History {
constructor (route, base) {
this.current = ' ';
this.app = new _Vue({
data() {
return {
path: '/'}}})}setupListeners() {
const handleRoutingEvent = () = > {
this.current = location.pathname? .slice(1) | |'/';
this.app.path = this.current;
}
window.addEventListener(
'popstate',
handleRoutingEvent
)
}
push (path) {
history.pushState({}, ' ', path);
this.app.path = path; }}class HashHistory {
constructor (route, base) {
this.current = ' ';
this.app = new _Vue({
data() {
return {
path: '/'}}})}setupListeners() {
const handleRoutingEvent = () = > {
this.current = location.hash? .slice(1) | |'/';
this.app.path = this.current;
}
window.addEventListener(
'hashchange',
handleRoutingEvent
)
}
push (location) {
window.location.hash = location
}
}
class Router {
constructor(options) {
this.options = options;
let mode = options.mode || 'hash';
this.routes = options.routes;
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
default:
/ / an error}}init() {
this.history.setupListeners()
}
push (location) {
this.history.push(location)
}
}
Router.install = function(Vue) {
_Vue = Vue;
Vue.mixin({
beforeCreate () {
if (this.$options.router) {
this._routerRoot = this
this._router = this.$options.router
this._router.init(this)}else {
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this}}})Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
Vue.component('router-view', {render(h){
const path = this._routerRoot._router.history.app.path;
const routes = this._routerRoot._router.routes;
const route = routes.find((i) = > i.path === ` /${path}`)
const com = route ? route.component : routes.find((i) = > i.path === ` ` / 404).component
return h(com)
}
})
}
export default Router;
Copy the code