在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
複製代碼