Vue插件開發
我們開發的插件需要實現一個install方法,它接收的第一個參數是Vue的構造器;通過全局方法 Vue.use()
使用插件。它需要在你調用 new Vue()
啓動應用之前完成。
hash模式和history模式
在html5的history模式出現以前,前段路由的實現基本都是使用hash來實現的,它能兼容到IE8。hash指的是url中’#'號及其後面的字符,也稱作錨點,可以使對應的元素顯示在可視區域內。當hash值變化時,瀏覽器不會向服務器發出請求。監聽hash的變化,可以使用hashchange事件。vue-router默認使用hash模式。
history模式在URL裏面不會有一個’#'號,html5規範提供了history.pushState和history.replaceState來進行路由控制,也不會向服務器發送請求,它只能兼容到IE10。
模仿vue-router插件
我們需要實現一個使用hash模式的路由插件,它要註冊兩個全局組件,k-router-link和k-router-view,並且能監聽hash的變化,將對應的dom顯示到頁面上。
代碼實現
-
配置一個路由文件
import Vue from 'vue' import HelloWorld from '@/components/HelloWorld.vue' import About from '@/components/About.vue' // 導入我們實現的router組件 import KVueRouter from './KVueRouter' // use方法安裝KVueRouter插件,如果KVueRouter是個對象,需要實現一個install方法 Vue.use(KVueRouter) // 配置路由選項 const router1 = [ { path: '/', name: 'home', component: HelloWorld }, { path: '/about', name: 'about', component: About } ] export default new KVueRouter({ router1 })
-
在KVueRouter.js文件中,創建並導出一個KVueRouter類,實現install方法
import KRouterLink from './KRouterLink.js' import KRouterView from './KRouterView.js' // 聲明一個變量來接收install方法中收到的Vue構造函數 let Vue // new KVueRouter的時候會調用constructor方法 class KVueRouter { constructor(options) { // options就是配置的路由信息,將它作爲KVueRouter對象的一個屬性 this.$options = options // 創建一個響應式數據,來存儲當前的路由信息,在KRouterView組件中可以直接用這個變量 // defineReactive()方法是vue創建響應式數據的方法,這裏是在KVueRouter對象上面創建一個名爲current的響應式屬性,初始值是'/' Vue.util.defineReactive(this, 'current', '/') // 使用hashchange事件來監聽當前路由的變化,它監聽的是當前連接的錨部分(就是 # 後面的)的變化 // 使用bind方法防止this指向發生變化 window.addEventListener('hashchange', this.onHashChange.bind(this)) window.addEventListener('load', this.onHashChange.bind(this)) // 生成一個map,方便view組件獲取當前路由對應的組件 this.routerMap = {} this.$options.router1.forEach(route => { this.routerMap[route.path] = route.component }) } onHashChange () { // window.location.hash就是url中錨部分,但是它以# 開頭,需要把#去掉 this.current = window.location.hash.slice(1) this.match = [] this.matchRouter() } } // 實現Vue.use()需要的install方法 // Vue.use()方法會把Vue作爲參數傳到install方法中來 KVueRouter.install = function (_vue) { Vue = _vue // 此時收到的Vue是個構造函數,並不是根實例,這裏需要把根實例中的router選項掛載到Vue原型鏈上,這樣每個組件都可以通過this.$router來訪問router // 只能利用全局混入,在beforeCreate()這個聲明週期鉤子中獲取router選項 Vue.mixin({ // beforeCreate鉤子在每個組件實例化的時候去執行,但是router選項只在根實例中有,所以要判斷一下 beforeCreate () { // 這裏this指向當前正在實例化的組件,this.$options是組件的初始化選項 if (this.$options.router) { Vue.prototype.$router = this.$options.router } } }) // 再聲明兩個全局組件,link和view Vue.component('k-router-link', KRouterLink) Vue.component('k-router-view', KRouterView) } export default KVueRouter
-
實現k-router-link組件
// 導出一個對象,這個對象是link組件的配置信息 // 這裏渲染一個<a href="/a/b"></a> // 這個組件從父組件接收一個to屬性,來判斷自己的地址 export default { props: { to: { type: String, required: true } }, // 使用render函數 render (h) { // h其實是createElement(),它接受三個參數: 標籤名稱、屬性集合、子元素數組 // 我們使用前端hash路由模式來實現一個單頁面應用,需要在接收到的路由字符串前邊拼接一個'#' // this.$slots存放了插槽的內容 return h('a', { attrs: { href: '#' + this.to } }, [this.$slots.default]) // 這裏還可以使用jsx語法,但是有使用的限制,vue-cli支持這種寫法 // return <a href={'#' + this.to}>{this.$slots.default}</a> } }
-
實現k-router-view組件
// 這個組件就是獲取當前路由對應的組件,拿到這個組件對象,並渲染到頁面中 export default { // 只有一層路由的情況 render (h) { let component = null // 通過this.$router.routerMap獲取創建的路由map,通過this.$router.current獲取當前鏈接 let route = this.$router.routerMap[this.$router.current] if (route) { component = route } return h(component) } }
使用
和使用router-view一樣,我們在main.js中導入路由,並注入到根實例中,這樣整個應用都擁有了路由功能
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './kvuerouter/index.js'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
App.vue
<template>
<div id="app">
<k-router-link to="/">router home |</k-router-link>
<k-router-link to="/about">router about</k-router-link>
<k-router-view></k-router-view>
</div>
</template>