經過上篇,咱們知道前端理由的兩種實現方法,Hash 路由與 History 路由,而且用它們分別實現了一個前端路由。html
接下來咱們就將 Vue 與 Hash 路由結合,實現一個很是簡單的 vue-router 吧。前端
想象一下,若是本身實現了一個 vue-router,會怎麼去使用呢?參考 vue-router 官方的使用方式,看看 html 的使用:vue
<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 的:git
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 做爲參數傳入,而不是注入掛載到根實例下。github
接下來就是 VueRouter 的實現了。vue-router
要怎麼來實現 VueRouter 呢,先提供一下實現的思路:app
hashchange
事件,實現前端路由;router-view
會響應更新;router-link
和 router-view
組件。先建立一個 VueRouter:ui
class VueRouter {
constructor (Vue, options) {
this.$options = options;
}
}
複製代碼
給 VueRouter 添加一個綁定事件的方法,一旦路由發生改變,會觸發 onHashChange
方法。this
constructor (Vue, options) {
this.init();
}
// 綁定事件
init () {
window.addEventListener('load', this.onHashChange.bind(this), false);
window.addEventListener('hashchange', this.onHashChange.bind(this), false);
}
複製代碼
將傳入的 options 設置成一張路由映射表,以便於經過路由查找到對應的組件。spa
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。
所有源碼參考 這裏。