前言
通過上篇,我們知道前端理由的兩種實現方法,Hash 路由與 History 路由,並且用它們分別實現了一個前端路由。
接下來我們就將 Vue 與 Hash 路由結合,實現一個非常簡單的 vue-router 吧。
開始實現
想象一下,如果自己實現了一個 vue-router,會怎麼去使用呢?參考 vue-router 官方的使用方式,看看 html 的使用:
<div id="app">
<p>
<router-link to="#/">home</router-link>
<router-link to="#/book">book</router-link>
<router-link to="#/movie">movie</router-link>
</p>
<router-view></router-view>
</div>
複製程式碼
這裡會有 router-link
和 router-view
兩個元件需要我們來實現。再來看 js 的:
const Home = { template: '<div>home</div>' };
const Book = { template: '<div>book</div>' };
const Movie = { template: '<div>movie</div>' };
const routes = [
{ path: '/', component: Home },
{ path: '/book', component: Book },
{ path: '/movie', component: Movie }
];
const router = new VueRouter(Vue, {
routes
});
new Vue({
el: '#app'
});
複製程式碼
這裡會有我們自己定義的元件 Home、Book 和 Movie,並且有它們各自對應的路由。我們實現的 VueRouter 跟官方的有些區別,在 VueRouter 被 new 時是將 Vue 作為引數傳入,而不是注入掛載到根例項下。
接下來就是 VueRouter 的實現了。
VueRouter
要怎麼來實現 VueRouter 呢,先提供一下實現的思路:
- 繫結
hashchange
事件,實現前端路由; - 將傳入的路由和元件做一個路由對映,切換哪個路由即可找到對應的元件顯示;
- 需要 new 一個 Vue 例項還做響應式通訊,當路由改變的時候,
router-view
會響應更新; - 註冊
router-link
和router-view
元件。
先建立一個 VueRouter:
class VueRouter {
constructor (Vue, options) {
this.$options = options;
}
}
複製程式碼
繫結事件
給 VueRouter 新增一個繫結事件的方法,一旦路由發生改變,會觸發 onHashChange
方法。
constructor (Vue, options) {
this.init();
}
// 繫結事件
init () {
window.addEventListener('load', this.onHashChange.bind(this), false);
window.addEventListener('hashchange', this.onHashChange.bind(this), false);
}
複製程式碼
路由對映表
將傳入的 options 設定成一張路由對映表,以便於通過路由查詢到對應的元件。
constructor (Vue, options) {
this.$options = options;
this.routeMap = {};
this.createRouteMap(this.$options);
}
// 路由對映表
createRouteMap (options) {
options.routes.forEach(item => {
this.routeMap[item.path] = item.component;
});
}
複製程式碼
options 之中,路由與元件的關係:
const routes = [
{ path: '/', component: Home },
{ path: '/book', component: Book },
{ path: '/movie', component: Movie }
];
複製程式碼
生成的路由對映表:
this.routeMap = {
'/': Home,
'/book': Book,
'/movie': Movie
};
複製程式碼
響應
我們需要 new 一個新的 Vue 例項,將當前路由 current
儲存在其 data 之中,當修改了 current
時,router-view
就會自己去更新檢視。
constructor (Vue, options) {
this.app = new Vue({
data: {
current: '#/'
}
});
}
// 獲取當前 hash 串
getHash () {
return window.location.hash.slice(1) || '/';
}
// 設定當前路徑
onHashChange () {
this.app.current = this.getHash();
}
複製程式碼
只要在 router-view
裡使用到了 this.app.current
,一旦更新它,便會更新。
註冊元件
router-link
實際上就是一個 <a> 標籤,點選它便能觸發 hashchange
。router-view
會實現一個 render 方法,將當前路由對應的元件取出,進行渲染。
constructor (Vue, options) {
this.initComponent(Vue);
}
// 註冊元件
initComponent (Vue) {
Vue.component('router-link', {
props: {
to: String
},
template: '<a :href="to"><slot></slot></a>'
});
const _this = this;
Vue.component('router-view', {
render (h) {
var component = _this.routeMap[_this.app.current];
return h(component);
}
});
}
複製程式碼
完整程式碼
至此,一個簡單的 vue-router 就出來了,全部程式碼是這樣的:
class VueRouter {
constructor (Vue, options) {
this.$options = options;
this.routeMap = {};
this.app = new Vue({
data: {
current: '#/'
}
});
this.init();
this.createRouteMap(this.$options);
this.initComponent(Vue);
}
// 繫結事件
init () {
window.addEventListener('load', this.onHashChange.bind(this), false);
window.addEventListener('hashchange', this.onHashChange.bind(this), false);
}
// 路由對映表
createRouteMap (options) {
options.routes.forEach(item => {
this.routeMap[item.path] = item.component;
});
}
// 註冊元件
initComponent (Vue) {
Vue.component('router-link', {
props: {
to: String
},
template: '<a :href="to"><slot></slot></a>'
});
const _this = this;
Vue.component('router-view', {
render (h) {
var component = _this.routeMap[_this.app.current];
return h(component);
}
});
}
// 獲取當前 hash 串
getHash () {
return window.location.hash.slice(1) || '/';
}
// 設定當前路徑
onHashChange () {
this.app.current = this.getHash();
}
}
複製程式碼
最後
將 Vue 與 Hash 路由結合,監聽了 hashchange
事件,再通過 Vue 的 響應機制
和 元件
,便有了上面實現好了一個 vue-router。
全部原始碼參考 這裡。