隨着前端業務的發展,
咱們通常在寫一個較爲大型的vue
項目時候,會使用到vue-router
,來根據指定的url
或者hash
來進行內容的分發,能夠達到不像服務端發送請求,就完成頁面內容的切換,可以減小像服務器發送的請求,讓用戶進行頁面跳轉時候可以更快,體驗更好前端
在初學vue-router
的時候,通常人都會有一個印象,router-link
以及router-view
都是vue
原生自帶的標籤。可是這個印象是錯誤的,vue-router
本質上是一個vue
的插件,經過Vue.use(VueRouter)
來使用這個插件。router-link
以及router-view
也是這個插件實現的自定義標籤。vue
本文以開發插件的模式,擼一個vue-router
插件以加深對其原理的瞭解node
也就是說,要實現一個簡單的vue-router
,須要完成如下需求webpack
vue create my-vue-router
因爲只着重於vue-router
的內容,因此先使用本來的vue-router
這樣只替換vue-router
源碼文件便可git
增長vue-router
github
vue add router
而後項目目錄就變成了web
my-vue-router |- node_modules |- public |- src |- assets |- components |- HellowWorld.vue |- router |- index.js |- views |- About.vue |- Home.vue |- App.vue |- main.js |- .gitinore |- babel.config.js |- package.json |- README.md |- yarn.lock
在目錄中,新建一個myRouter.js
的文件,來放置咱們的源碼vue-router
my-vue-router |- node_modules |- public |- src |- assets |- components |- HellowWorld.vue |- router |- index.js + |- myRouter.js |- views |- About.vue |- Home.vue |- App.vue |- main.js |- .gitinore |- babel.config.js |- package.json |- README.md |- yarn.lock
此時,@/src/router/index.js
中的內容裏,咱們將導入的vue-router
替換爲咱們的myRouter.js
json
import Vue from 'vue' - import VueRouter from 'vue-router' + import VueRouter from './myRouter' import Home from '../views/Home.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
這裏咱們能夠看到,代碼執行的流程爲
引入myRouter.js
->配置routes對象
->new VueRouter
->export default
導出api
此處用到了 Vue.use()
這個API
vue
中的插件,一個核心的api
就是vue.use()
安裝 Vue.js 插件。若是插件是一個對象,必須提供 install 方法。若是插件是一個函數,它會被做爲install 方法。install 方法調用時,會將 Vue 做爲參數傳入。
該方法須要在調用 new Vue() 以前被調用。
當 install 方法被同一個插件屢次調用,插件將只會被安裝一次。
也就是說,咱們在本身造的myRouter
裏得實現這個install
方法
new VueRouter
來生成實例url
變化,並雙向綁定current方法router-link
與router-view
let Vue;//因爲使用者確定是使用vue.use引入的這個插件,因此代碼裏就不引入vue.js了,防止重複打包 // 需求1 聲明一個擁有constructor構造器的class class VueRouter{ constructor(options={}){// 構造函數 this.$options=options;// 保存配置項 this.app = { // 聲明一個擁有current的變量,已完成路由的雙向綁定 current:"/" } Vue.util.defineReactive(this.app,'current',this.app.current);//vue的攔截方法,會該值增長get攔截以收集依賴,set攔截以觸發雙向綁定 this.routerMap={}; // 建立key-value模式的routerMap,便於使用key可以快速的找到即將render(渲染)的組件 this.init(options); // 執行init方法,以完成需求3,4,5 } init(options={}){ this.bindBrowserEvents()// 綁定瀏覽器事件 this.initComponent()//註冊router-view及router-link組件 this.createRouterMap(options.routes)//建立key-value模式的routerMap } createRouterMap(arr=[]){ // 建立routerMap arr.forEach(item => { this.routerMap[item.path]=item }); // 處理完後routerMap格式以下 // this.routerMap = { // '/':{ // path: '/', // name: 'Home', // component: Home // }, // '/about':{ // path: '/about', // name: 'About', // component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') // } // } } bindBrowserEvents(){ // hash模式監聽 hashchange 方法 window.addEventListener('load',this.onHashChange.bind(this)) window.addEventListener('hashchange',this.onHashChange.bind(this)) } initComponent(){ // 註冊自定義組件RouterLink及RouterView Vue.component('RouterLink',{ props: { to: String }, render(h) { return h('a',{ attrs:{ href:'#'+this.to } },this.$slots.default) }, }) Vue.component('RouterView',{ render:(h)=>{ const component = this.routerMap[this.app.current].component return h(component) }, }) } onHashChange(){ // hash變化時,改變 this.app.current window.location.hash = window.location.hash || '/'; // 若是hash沒有值,則默認給補一個/#/ if(this.routerMap[window.location.hash.slice(1)]){ // this.app.current = hash值 this.app.current = window.location.hash.slice(1); }else{ this.app.current = '/'; } // 此處執行完後,則因爲雙向綁定,會觸發routerView進行從新渲染 } } // 需求2 實現install方法 VueRouter.install = function(_Vue){ Vue = _Vue; // 由於必定會先走install,因此將這個傳入的Vue實例,保存到變量Vue中 }
註釋都寫在代碼裏啦,能夠執行簡單的路由雙向綁定功能,有哪裏有疑問能夠提出~互相學習~
以爲好的話,能夠給個人 github點個star
哦