首頁 > 軟體

深入瞭解vue-router原理並實現一個小demo

2022-03-03 16:00:22

外掛編寫的基本方法

推薦大家先看看官方給出的外掛使用和開發方法

https://vuejs.bootcss.com/guide/plugins.html​

需求分析

我們先看看vue-router的使用步驟

1.use

Vue.use(VueRouter)

注意⚠️:

Vue.use()主要是呼叫外掛內部的install方法,並將Vue範例作為引數傳入​

2.new 一個router範例

const router = new VueRouter({
  // 範例化router傳入的引數
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

3.new Vue() ,把範例放在vue的設定項裡面

new Vue({
  router, // 注意router的範例也往裡傳
  render: h => h(App)
}).$mount('#app')

4.使用路由元件<router-view/><router-link></router-link>或者在元件中使用this.$router

由此我們看看vue-router內部做了什麼?

將$router掛載到全域性上實現並宣告了兩個元件:<router-view/><router-link></router-link>

實現思路

首先我們看看如何將$router掛載到元件上​

let Vue; // 儲存vue的建構函式,避免打包將其打進去
VueRouter.install = function (_Vue) {
  Vue = _Vue;
  console.log("options", Vue.$options);
  Vue.mixin({
    beforeCreate() {
      console.log("inner", this);
      console.log(" this.$options.router", this.$options.router);
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router;
      }
    },
  });
  console.log("end");
};

可以看到:

1、第一次執行的時候,即在Vue.use(Router)時,還沒有範例化vue(因為Vue.use()發生在 new Vue()之前),所以Vue.$option本身是拿不到的(ps: option就是new Vue()時傳入的引數,router也往裡面傳),此時既然拿不到router的範例,所以不能直接在install方法裡面掛載;

​2、我們可以在use的時候做一個全域性混入,在合適的時間點,獲取到Vue根範例設定項中的router範例, 執行掛載。緊接著在new Vue()根範例建立的時候,因為注入了router範例,所以再執行全域性混入(mixin)中的生命週期時,這個時候根範例的設定項this.$options已經包含了router範例,可以此時把router掛載到Vue的原型上。之後所有Vue範例擴充套件來的VueCompont都可以通過this.$router存取到這個屬性

​如何實現那兩個路由元件

先看看路由元件如何使用

<div id="app">
  <div id="nav">
    <!-- a標籤控制跳轉 -->
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </div>
  <!-- 路由出口 -->
  <router-view />
</div>

由上面可以看出,點選router-link,就相當於點了a標籤,然後a標籤的href屬性控制頁面路由發生了變化;監聽路由變化,然後仔router-view裡面輸出不同的模板;​

先來看看router-link

class VueRouter {
  constructor(options) {
    // 接受傳入的引數
    this.$options = options;
    const initial = "/";
    // 將current變成響應式資料,
    //這樣在hashchange的回掉中修改curent時,
    //用到current的router-view的render函數就會重新渲染
    Vue.util.defineReactive(this, "current", initial);
    // 監聽路由變化
    window.addEventListener("hashchange", () => {
      // 獲取當前url中的hash
      this.current = window.location.hash.slice(1);
    });
  }
}

VueRouter.install = function (_Vue) {
  Vue = _Vue;
  Vue.component("router-view", {
    render(h) {
      // 獲取當前路由所對應的元件,然後把它渲染出來
      const { current, $options } = this.$router;
      // 這裡要注意 我們傳進來的routes是一個路由表,如下圖一
      // 所以這裡我們是找出匹配到當前current路由的項,然後直接渲染元件
      const route = $options.routes.find((item) => {
        return item.path === current;
      });
      let component = route ? route.component : null;

      return h(component);
    },
  });
}

​再來看看router-view

class VueRouter {
  constructor(options) {
    // 接受傳入的引數
    this.$options = options;
    const initial = "/";
    // 將current變成響應式資料,
    //這樣在hashchange的回掉中修改curent時,
    //用到current的router-view的render函數就會重新渲染
    Vue.util.defineReactive(this, "current", initial);
    // 監聽路由變化
    window.addEventListener("hashchange", () => {
      // 獲取當前url中的hash
      this.current = window.location.hash.slice(1);
    });
  }
}

VueRouter.install = function (_Vue) {
  Vue = _Vue;
  Vue.component("router-view", {
    render(h) {
      // 獲取當前路由所對應的元件,然後把它渲染出來
      const { current, $options } = this.$router;
      // 這裡要注意 我們傳進來的routes是一個路由表,如下圖一
      // 所以這裡我們是找出匹配到當前current路由的項,然後直接渲染元件
      const route = $options.routes.find((item) => {
        return item.path === current;
      });
      let component = route ? route.component : null;

      return h(component);
    },
  });
}

圖一

完整demo程式碼

// 我們要實現什麼
// 1、外掛
// 2、兩個元件

// 儲存vue的建構函式,避免打包將其打進去
let Vue;
class VueRouter {
  constructor(options) {
    this.$options = options;
    const initial = "/";
    Vue.util.defineReactive(this, "current", initial);
    this.current = "/";
    window.addEventListener("hashchange", () => {
      // 獲取當前url中的hash
      this.current = window.location.hash.slice(1);
    });
  }
}

// 引數1在Vue.use()呼叫時傳進來,
VueRouter.install = function (_Vue) {
  Vue = _Vue;
  console.log("options", this);

  // 全域性混入
  // 目的:延遲下面的邏輯 到 router建立完畢並且附加到選項上時才執行
  Vue.mixin({
    // 在每個元件建立範例時都會執行
    beforeCreate() {
      // this.$options.router ;即new Vue時放進去的router範例
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router;
      }
    },
  });

  // 註冊並且實現兩個元件
  Vue.component("router-link", {
    props: {
      to: {
        required: true,
      },
    },
    render(h) {
      return h(
        "a",
        {
          attrs: { href: "#" + this.to },
        },
        this.$slots.default
      );
    },
  });
  Vue.component("router-view", {
    render(h) {
      // 獲取當前路由所對應的元件,然後把它渲染出來
      const { current, $options } = this.$router;
      const route = $options.routes.find((item) => {
        return item.path === current;
      });
      let component = route ? route.component : null;

      return h(component);
    },
  });
};

export default VueRouter;

總結

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!     


IT145.com E-mail:sddin#qq.com