核心版vue-router, 僅僅只需80行代碼

簡介

vue-router做爲vue全局桶的一個核心類庫, 其實現方式, 有着不少巧妙之處, 你是否真正瞭解呢?javascript

使用一個vue-router的正確姿態.

最簡單的是經過cli安裝一個vue-router插件, 會自動的修改咱們項目的代碼, 正確的使用vue-router. 一條命令搞定.vue

vue add router
複製代碼

正確的姿態.

  1. 新建一個路由配置文件.
import Vue from 'vue'
import VueRouter from 'vue-router'
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({
  routes
})

export default router

複製代碼
  1. 根組件, 掛載router.
import Vue from 'vue'
import App from './App.vue'
import router from './router';

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

複製代碼
  1. 使用router-link作導航, router-view作佔位.

看起來是否很簡單? 答案是確定的. 由於以上步驟都是安裝vue-router插件時, 會自動幫咱們添加的.java

咱們的問題是?

  1. Vue.use(VueRouter), 這行代碼是幹什麼用? 爲何要放在頂部執行, 放到後面行不行?
  2. 在入口處, 爲何要傳入router? 是幹什麼用?
  3. router-link, router-view組件在哪裏定義的? 爲何咱們能直接用?
  4. 路由更改後, 是怎樣渲染對於組件的?

核心版vue-router.

lib/x-vue-router.jsnode

/** * - 建立一個插件 * - vueRouter是一個class, 能夠new一個實例 * - $router掛載到vue的原型上. * - 監聽hash變化, 能夠及時響應 * - router-link, router-view兩個全局組件 */
let Vue;
class XVueRouter {
  constructor(options) {
    this.$options = options;

    // 保存path和組件的對應關係.
    this.routeMap = {};

    // 使用vue來作數據響應式. curren保存當前的url hash
    // 一旦hash發生改變, 經過onHashChange更改current值, 對應的router-view組件的
    // render方法就會被從新執行(也就是新的組件就會從新渲染, 路徑切換成功). 
    this.vm = new Vue({
      data: {
        current: '/'
      }
    });

    this.onHashChange = this.onHashChange.bind(this);
  }

  init() {
    // hashchange
    this.bindEvents();

    // 初始化path和組件的鍵值對
    this.initRouteMap();

    // 添加router-link, router-view組件.
    this.createGlobalComponent();
  }

  bindEvents() {
    window.addEventListener('hashchange', this.onHashChange);
    window.addEventListener('load', this.onHashChange);
  }

  initRouteMap() {
    this.$options.routes.forEach(m => this.routeMap[m.path] = m.component);
  }

  onHashChange() {
    // #/about -> /about
    this.vm.current = window.location.hash.slice(1) || '/';
  }

  createGlobalComponent() {
    Vue.component('router-link', {
      props: { to: { type: String, required: true } },
      render() {
        return <a href={`#${this.to}`}>{this.$slots.default}</a>;
      }
    });

    Vue.component('router-view', {
      render: h => h(this.routeMap[this.vm.current])
    });
  }
}

/** * 建立一個新插件. 實現一個靜態的install方法. */
XVueRouter.install = function (_Vue) {
  // 經過Vue.use方法來安裝插件時, 會傳入一個Vue構造器.
  Vue = _Vue;

  // 調用vue的mixin方法, 在組件的beforeCreate生命週期中
  // 初始化vueRouter.
  Vue.mixin({
    beforeCreate() {
      // this指向的是組件的實例(這裏解析了爲何要在跟組件中傳入router實例.)
      if (this.$options.router) {
        // 1. 將$router掛載到vue的原型, 方便組件內部直接調用.
        Vue.prototype.$router = this.$options.router;

        // 2. 初始化vueRouter.
        this.$options.router.init();
      }
    }
  })
}

export default XVueRouter;
複製代碼

看完這80行代碼後, 以上的4個問題是否已有答案了呢?

code

demowebpack

相關文章
相關標籤/搜索