github
Vue-router
是Vue.js
官方的路由管理器。它和Vue.js
的核心深度集成,讓構建單頁面應用變得易如反掌。javascript
vue add router
步驟一:使用vue-router
插件vue
//router.js import Router from 'vue-router';
*/
Vue.use(Router); // 引入插件java
步驟二:建立Router實例git
// router.js export default new Router({...}) // 導出Router實例
步驟三:在根組件添加該實例github
// main.js import router from './router'; new Vue({ router // 添加到配置項 }).$mount("#app")
步驟四:添加路由視圖vue-router
<!-- App.vue --> <router-view></router-view>
步驟五:導航api
<router-link to="/">Home</router-link> <router-link to="/about">About</router-link>
this.$router.push('/'); this.$router.push('/about')
單頁面應用程序中,url
發生變化時候,不能刷新,顯示對應視圖app
#/about
/about
根據url
顯示對應的內容函數
router-view
current
變量持有url
地址,一旦變化,動態執行render
實現一個插件優化
VueRouter
類url
變化install
方法$router
註冊在Vue2.x
項目中的src
路徑下,複製一份router
文件,重命名爲ou-router
。
而後在ou-router
路徑下新建一個ou-vue-router.js
文件,並將index.js
文件中的VueRouter
引入改成ou-vue-router.js
。
import VueRouter from './ou-vue-router'
同時將main.js
中的router
引入也修改一下。
import router from './ou-router'
關於Vue插件的建立:
function
實現,也可使用object
或class
實現;要求必須有一個install
方法,未來會被Vue.use()
使用
let Vue; // 保存Vue的構造函數,插件中須要用到 class VueRouter {}
*/
VueRouter.install = function (_Vue) {
Vue = _Vue; // 引用構造函數,VueRouter中要使用
}
export default VueRouter;
### 掛載`$router` 當咱們發現`vue-router`引入`vue`的時候,第一次是在`router/index.js`中使用了`Vue.use(Router)`,在這個時候也就會調用了`vue-router`的`install`方法;而第二次則是在`main.js`中,建立根組件實例的時候引入`router`,即`new Vue({router}).$mount("#app")`。 也就是說,當調用`vue-router`的`install`方法的時候,項目尚未建立`Vue`的根組件實例。所以咱們須要在`vue-router`的`install`方法使用全局混入,延遲到`router`建立完畢才執行掛載`$router`。
let Vue; // 保存Vue的構造函數,插件中須要用到
class VueRouter {}
/*
*/
VueRouter.install = function (_Vue) {
Vue = _Vue; // 引用構造函數,VueRouter中要使用 /* 掛載$router */ /* * 全局混入 * 全局混入的目的是爲了延遲下面邏輯到router建立完畢而且附加到選項上時才執行 * */ Vue.mixin({ beforeCreate() { // 此鉤子在每一個組件建立實例時都會調用 /* this.$options即建立Vue實例的第一個參數 */ if(this.$options.router){ // 只在根組件擁有router選項
Vue.prototype.$router = this.$options.router; // vm.$router
} } })
}
export default VueRouter;
### 註冊全局組件`router-link`和`router-view` 首先在`install`方法中註冊兩個全局變量。
let Vue;
class VueRouter {}
VueRouter.install = function (_Vue) {
Vue = _Vue; Vue.mixin({ ... }) /* 註冊全局組件router-link和router-view */ Vue.component('router-link',{ render(createElement){ return createElement('a','router-link'); // 返回虛擬Dom } }); Vue.component('router-view',{ render(createElement){ return createElement('div','router-view'); // 返回虛擬Dom } })
}
export default VueRouter;
router-view
是一個a
標籤router-view
的to
屬性設置到a
標籤的herf
屬性(先默認使用hash
方法)獲取router-view
的插槽內容,插入a
標籤中
Vue.component('router-link', { props: { to: { type: String, required: true } }, render(createElement) { // 返回虛擬Dom return createElement('a', { attrs: {href: '#' + this.to} // 設置a標籤的href屬性 }, this.$slots.default // 獲取標籤插槽內容 ); } });
router-view
router-view
實質上根據url
的變化,實時響應渲染對應的組件,而createElement
函數是能夠傳入一個組件參數的。
所以,咱們不進行渲染任何內容,後面實現監聽url
變化後,從映射表獲取到組件後,再來實現router-view
。
Vue.component('router-view', { render(createElement) { let component = null; return createElement(component); // 返回虛擬Dom } })
url
變化咱們在VueRouter
類的constructor
函數中監聽url
的變化,這裏咱們默認使用hash
方式。
並且,咱們須要將存入url
的變量設置爲響應式數據,這樣子當其發生變化的時候,router-view
的render
函數纔可以再次執行。
class VueRouter { /* * options: * mode: 'hash' * base: process.env.BASE_URL * routes * */ constructor(options) { this.$options = options; // 將current設置爲響應式數據,即current變化時router-view的render函數可以再次執行 const initial = window.location.hash.slice(1) || '/'; Vue.util.defineReactive(this, 'current',initial); // 監聽hash變化 window.addEventListener('hashchange', () => { this.current = window.location.hash.slice(1); }) } }
所以,咱們能夠來實現router-view
組件。
在render
函數中,this.$router
指向的是VueRouter
建立的實例,所以咱們能夠經過this.$router.$option.routes
獲取路由映射表,this.$router.current
獲取當前路由,而後經過遍歷匹配獲取組件。
Vue.component('router-view', { render(createElement) { let component = null; // 獲取當前路由對應的組件 const route = this.$router.$options.routes .find(route => route.path === this.$router.current); if (route) { component = route.component; } return createElement(component); // 返回虛擬Dom } })
history
模式前面的實現都默認爲hash
模式,接下來簡單實現一下history
模式。
首先將監聽url
的代碼優化一下,並判別mode
的值來設置current
的初始值,而history
模式下初始值爲window.location.pathname
。
class VueRouter { /* * options: * mode: 'hash' * base: process.env.BASE_URL * routes * */ constructor(options) { this.$options = options; switch (options.mode) { case 'hash': this.hashModeHandle(); break; case 'history': this.historyModeHandle(); } } // Hash模式處理 hashModeHandle() { // 將current設置爲響應式數據,即current變化時router-view的render函數可以再次執行 const initial = window.location.hash.slice(1) || '/'; Vue.util.defineReactive(this, 'current', initial); // 監聽hash變化 window.addEventListener('hashchange', () => { this.current = window.location.hash.slice(1); }) } // History模式處理 historyModeHandle() { const initial = window.location.pathname || '/'; Vue.util.defineReactive(this, 'current', initial); } }
而後咱們來實現history
模式下的router-link
組件。
在history
模式下,當咱們點擊router-link
時,即點下a
標籤時,頁面會從新刷新。因此咱們須要設置一下其點擊事件,取消默認事件,而後經過history.pushState
去修改url
,而後重設current
的值。
Vue.component('router-link', { render(createElement) { // 返回虛擬Dom const self = this; const route = this.$router.$options.routes .find(route => route.path === this.to); return createElement('a', { attrs: {href: this.to}, // 設置a標籤的href屬性 on: { click(e) { e.preventDefault(); // 取消a標籤的默認事件,即刷新頁面 history.pushState({}, route.name, self.to); // 經過history.pushState來改變url self.$router.current = self.to; } } }, this.$slots.default // 獲取標籤插槽內容 ); } })
最後咱們將兩種模式的router-link
組件進行一個合併。
Vue.component('router-link', { props: { to: { type: String, required: true } }, render(createElement) { // 返回虛擬Dom if(this.$router.$options.mode === 'hash'){ return createElement('a', { attrs: {href: '#' + this.to} // 設置a標籤的href屬性 }, this.$slots.default // 獲取標籤插槽內容 ); }else{ const self = this; const route = this.$router.$options.routes .find(route => route.path === this.to); return createElement('a', { attrs: {href: this.to}, // 設置a標籤的href屬性 on: { click(e) { e.preventDefault(); // 取消a標籤的默認事件,即刷新頁面 history.pushState({}, route.name, self.to); // 經過history.pushState來改變url self.$router.current = self.to; } } }, this.$slots.default // 獲取標籤插槽內容 ); } } });