手寫一個vue-router

舜嶽發表於2020-10-04

實現效果如下:

1. 準備好環境

使用 vue/cil 初始化專案配置:

npm install -g @vue/cli    //全域性安裝@vue/cli
vue create demo-vue        //建立專案

yarn add vue-router 安裝vue-router建立一個router資料夾並使用:

2. 實現目的

router/index.js內容如下:(我們的目的將引入自寫的vue-router實現vue路由跳轉功能)

import vue from 'vue'
//import vueRouter from 'vue-router'       //使用官方 vue-router 外掛
import vueRouter from './my-vue-router'    //使用我們直接實現的 vue-router 外掛

vue.use(vueRouter)                         //此時執行 my-vue-router 內 install 方法

const routes = [
  {
    path: '/',
    redrect: '/a'
  },
  {
    path: '/a',
    name: 'a',
    component: () => import('../views/a.vue')
  },
  {
    path: '/b',
    name: 'b',
    component: () => import('../views/b.vue')
  }
]

const router = new vueRouter({
  mode: 'hash',        //雜湊路由
  routes
})

export default router  //main.js 引入例項化的 router 新增至 this.$options.router 中

3. 實現原理

router/my-vue-router.js 實現過程如下:

let Vue //宣告一個變數用來儲存install內接收的vue例項 給 constructor 內呼叫

class vueRouter {
  constructor(options) {
    this.$options = options    //用於通過全域性 this.$options.router.$options 獲取

    const initail = window.location.hash.slice(1) || '/'
    Vue.util.defineReactive(this, 'current', initail)    //監聽資料響應式渲染頁面
    window.addEventListener('hashchange', () => {        //監聽雜湊路由變化
      this.current = window.location.hash.slice(1)       //改變觸發 render 渲染頁面
    })
  }

  push() {
    console.log("跳轉頁面");
  }
}

vueRouter.install = function(_vue) {
  Vue = _vue

  Vue.mixin({
    beforeCreate() {
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router  	//將全域性資料注入元件
      }
    }
  })

  Vue.component('router-link', {                        //註冊全域性 router-link 元件
    props: {
      to: {
        type: String,
        required: true
      }
    },
    render(h) {
      return h(
        'a',
        {
          attrs: { href: '#' + this.to }
        },
        this.$slots.default
      )
    }
  })

  Vue.component('router-view', {                        //註冊全域性 router-view 元件
    render(h) {
      console.log(" this.$router",  this.$router);
      const route = this.$router.$options.routes.find((route) => {
        return route.path == this.$router.current
      })

      return h(route.component)                        //將元件渲染返回
    }
  })
}

export default vueRouter

3. 使用手寫vue-router:

a.vueb.vue 可任意寫

<template>
  <div id="app">
    <router-link to="/a">aaa</router-link> |
    <router-link to="/b">bbb</router-link>
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'App'
}
</script>

相關文章