在Vue中,vue-router占据重要的位置,是vue的核心插件,那么它的实现原理是什么呢? 在剖析原理以前,先来了解几个概念:SAP,路由模式 SPA(single page application):单一页面应用程序,有且只有一个完整的页面,当它在加载页面时,不会加载整个页面,而只更新某个指定的容器中内容(组件)javascript
随着ajax的流行,异步数据请求交互运行在不刷新浏览器的状况下进行。而异步交互体验更高的版本就是SPA--单页应用。单页应用不只在页面交互时不用刷新,并且在页面跳转时也是不用刷新,因而就有浏览前端路由。实现原理就是根据不一样的url进行解析,来匹配不一样的组件;但当url发生变化时就会形成页面的刷新。这就出现了hash,使用hash在改变url的状况下,保证页面的不刷新。 http://www.xxx.com/#/login
这种#,后面hash值发生变化,并不会致使浏览器向服务器发出请求,进而不会发生页面刷新。并且每当hash发生变化时,都会触发hashchange事件,经过这个事件能够知道hash值发生了什么变化,而后能够监听hashchange来实现更新页面部份内容大都操做前端
因在HTML5标准发布后,多了两个API,pushState,replaceState,经过这两个API能够改变url地址且不会发送请求。同时还有popstate事件,经过这些API就能以另外一种方式来实现前端路由,但原理与hash实现相同,但不会有**#**,所以当用户主动刷新页面之类操做时,仍是会给服务器发送请求,为避免这种状况,因此这实现须要服务器支持,需把全部路由都重定向到根页面vue
abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。 根据平台差别能够看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境作校验,若是发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,因此 在使用 vue-router 时只要不写 mode 配置便可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式。 (固然,你也能够明确指定在全部状况下都使用 abstract 模式)java
原理核心:更新视图而不从新请求页面。 vue-router实现单页面跳转,提供了三种方式:hash模式、history模式 abstract模式,根据mode参决定使用哪一种方式。 接下来详细剖析其原理 ajax
在这张流程图中分析vue-router实现原理,咱们将官网的vue-router挂载到Vue实例上,打印出来增长了什么: vue-router
总结起来,vue-router就是一个类,里面封装了一个mixin,定义了两个‘原型’,注册了两个组件。 在mixin中beforeCreate中定义**_routerRoot、_router、_route**,使其每一个组件都有_routerRoot,定义了两个原型是指在Vue.prototype上面定义的两个getter,也就是**route**,注册两个组件是指要用到的router-link、router-view后端
基本原理:数组
app.vue浏览器
<template>
<div id="app">
<router-link to='/home'>首页</router-link>
<br>
<router-link to="/about">关于</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'app'
};
</script>
复制代码
Home.vue缓存
<template>
<div>
home
</div>
</template>
<script>
export default {
name:'home'
}
</script>
复制代码
About.vue
<template>
<div>
about
</div>
</template>
<script>
export default {
name:'about'
}
</script>
复制代码
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './routers';
import store from './store'
Vue.config.productionTip = false
new Vue({
name:'main',
router,
store,
render: h => h(App)
}).$mount('#app')
复制代码
index.js
import Vue from 'vue';
import routes from './routes';
import VueRouter from './vue-router';
// 使用Vue.use就会调用install方法
Vue.use(VueRouter)
export default new VueRouter({
mode:"history",//hash,history
routes
})
复制代码
routes.js
import Home from '../views/Home.vue';
import About from '../views/About.vue';
export default [
{path:'/home',name:'home',component:Home},
{path:'/about',name:'about',component:About}
]
复制代码
vue-router.js
class HistoryRoute{
constructor(){
this.current=null
}
}
class VueRouter{
constructor(options){
this.mode=options.mode||"hash";
this.routes=options.routes||[];
this.routesMap=this.createMap(this.routes)
this.history=new HistoryRoute();
this.init();
}
init(){
if(this.mode==="hash"){
//使用的是hash路由
// console.log('使用的是hash模式')
// console.log(location.hash)
location.hash?"":location.hash="/"
window.addEventListener("load",()=>{
this.history.current=location.hash.slice(1);
// console.log('load==>',this.history.current)
})
window.addEventListener("hashchange",()=>{
this.history.current=location.hash.slice(1)
// console.log('hashchange==>',this.history.current)
})
}else{
//使用的history
location.pathname?"":location.pathname='/'
window.addEventListener("load",()=>{
this.history.current=location.pathname;
// console.log('load==>',this.history.current)
})
window.addEventListener("popstate",()=>{
this.history.current=location.pathname
// console.log('popstate==>',this.history.current)
})
}
}
push(){}
go(){}
back(){}
// createMap将数组转换为对象结构
createMap(routes){
return routes.reduce((memo,current)=>{
// memo刚开始是个空对象
memo[current.path]=current.component
return memo;
},{})
}
}
VueRouter.install=function(Vue){
Vue.mixin({
// 将每一个组件都混入一个 beforeCreate
beforeCreate() {
// 获取根组件
if(this.$options&&this.$options.router){
// 找到根组件
// 把当前实例挂载到_router上面
this._router = this.$options.router
this._root=this;
Vue.util.defineReactive(this,"xxx",this._router,history)
}else{
// main---->app----->home/about
this._root = this.$parent._root; //全部组件都有了router
}
Object.defineProperty(this,"$router",{
get(){
// console.log(this._root)
return this._root._router
}
})
Object.defineProperty(this,"$route",{
get(){
// console.log(this._root._router.history.current)
return {
current:this._root._router.history.current
}
}
})
},
}),
Vue.component("router-link",{
props: {
to:String
},
render(h){
let mode = this._self._root._router.mode;
return <a href={mode==='hash'?`#${this.to}`:this.to}>{this.$slots.default}</a>
}
}),
Vue.component("router-view",{
render(h) {
let current=this._self._root._router.history.current;
// console.log(this)
let routesMap=this._self._root._router.routesMap
return h(routesMap[current])
},
})
}
export default VueRouter
复制代码