手写vue - 简单VueRouter的实现

vue手写简单router

  • 如何手写一个简单的router
    • 一、什么是vue-router?
    • 二、vue-router的安装使用
      • 1.安装: vue add router
      • 2.核⼼步骤:
        • 1)步骤⼀:使⽤vue-router插件,router.js
        • 2)步骤⼆:创建Router实例,router.js
        • 3)步骤三:在根组件上添加该实例,main.js
        • 4)步骤四:添加路由视图,App.vue
        • 5)导航
    • 三、vue-router做了哪些事?
      • 1.需求分析和实现目标
        • 1)spa ⻚⾯不能刷新
        • 2)根据url显示对应的内容
      • 2.需要做哪些事?
        • 1)实现VueRouter类
        • 2)实现install⽅法
    • 四、手写router
      • 1.index.js文件
      • 2.jrouter.js文件
    • 五、总结

如何手写一个简单的router

首先,我们得弄弄清楚什么是vue-router?vue-router的使用方法和原理是什么?vue-router做了哪些事?最后我们才能参考手写一个router。

一、什么是vue-router?

Vue Router 是 Vue.js 官⽅的路由管理器。它和 Vue.js 的核⼼深度集成,让构建单⻚⾯应⽤变得易如反掌。

二、vue-router的安装使用

1.安装: vue add router

2.核⼼步骤:

1)步骤⼀:使⽤vue-router插件,router.js

import Router from 'vue-router'
Vue.use(Router)

2)步骤⼆:创建Router实例,router.js

export default new Router({
     ...})

3)步骤三:在根组件上添加该实例,main.js

import router from './router'
new Vue({
     
router,
}).$mount("#app");

4)步骤四:添加路由视图,App.vue

<router-view></router-view>

5)导航

<router-link to="/">Home</router-link> <router-link to="/about">About</router-link>
this.$router.push('/')
this.$router.push('/about')

三、vue-router做了哪些事?

1.需求分析和实现目标

单⻚⾯应⽤程序中,url发⽣变化时候,不能刷新,显示对应视图内容。

1)spa ⻚⾯不能刷新

hash #/about
History api /about

2)根据url显示对应的内容

router-view
数据响应式:current变量持有url地址,⼀旦变化,动态重新执⾏render。

2.需要做哪些事?

实现⼀个插件

1)实现VueRouter类

① 处理路由选项
② 监控url变化,hashchange
③ 响应这个变化

2)实现install⽅法

① $router注册
② 两个全局组件:router-link和router-view

四、手写router

1.index.js文件


import Vue from 'vue'
import VueRouter from './jrouter'
import Home from '../views/Home.vue'

// 1.VueRouter是一个插件?
// 内部做了什么:
//    1)实现并声明两个组件router-view  router-link
//    2) install: this.$router.push()
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'),
    childen: [
      {
     
        path: '/about/info',
        component: {
      render(h) {
      return h('div', 'is a info') } }
      }
    ]
  }
]

// 2.创建实例
const router = new VueRouter({
     
  mode: 'hash',
  base: process.env.BASE_URL,
  routes
})

export default router

2.jrouter.js文件

// vue插件可以由function构成,必须有install方法,该方法会被vue.use调用
// install方法会会接收一个vue对象参数
// 为了不打包vue,先声明,由install参数赋值
let Vue
class VueRoute {
     
    constructor(options) {
     
        this.$options = options

        // window.location.hash性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)
        // location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,
        // 用location.href=url就可以直接将页面重定向url。而location.hash则可以用来获取或设置页面的标签值。比如http://domain/#admin的location.hash="#admin"。
        this.current = window.location.hash.slice(1) || "/"
        // Vue.util.defineReactive可以定义一个响应式对象
        Vue.util.defineReactive(this, 'matcher', [])
        // 初始化路由数组
        this.match()

        window.addEventListener('hashchange', () => {
     
            this.current = window.location.hash.slice(1)
            // 路由切换时 初始化
            this.matcher = []
            this.match()
        })
    }

    // 处理matcher数组.
    match(routes) {
     
        routes = routes || this.$options.routes
        for (const route of routes) {
     
            // 当前地址是否为'/'
            if (route.path === '/' && this.current === '/') {
     
                this.matcher.push(route)
                return
            }
            // 当前地址是否不是'/',但包含该路由路径
            if (route.path !== '/' && this.current.indexOf(route.path) !== -1) {
     
                this.matcher.push(route)
                if (route.childen) {
     
                    this.match(route.childen)
                }
                return
            }
        }
    }
}

VueRoute.install = function (_Vue) {
     
    Vue = _Vue

    // 1.挂载$router。
    // 由于install会由use调用,调用时,VueRoter对象可能会创建,所以使用全局混入
    // VueRouter由根实例(app.vue中的vue对象)传入
    // 全局混入: 混入能够增加组件方法的复用性
    Vue.mixin({
     
        beforeCreate() {
     
            // 此钩子函数在每个组件创建之前都会执行
            // this.$options.router只有根组件存在
            if (this.$options.router) {
     
                Vue.prototype.$router = this.$options.router
            }
        },
    })

    // 注册实现组件router-view和router-link
    Vue.component('router-link', {
     
        props: {
     
            to: {
     
                type: String,
                required: true
            },
        },
        render(h) {
     
            return h('a', {
      attrs: {
      href: '#' + this.to } }, this.$slots.default)
        }
    })

    Vue.component('router-view', {
     
        render(h) {
     
            // 深度标记,作用:标记当前router-view的层次
            let depth = 0
            // 判断是否是router-veiw
            this.$vnode.data.routerVeiw = true
            let parent = this.$parent

            // 遍历该路由的深度
            while (parent) {
     
                const vnodeData = parent.$vnode && parent.$vnode.data
                if (vnodeData) {
     
                    if (vnodeData.routerVeiw) {
     
                        depth++
                    }
                }
                parent = parent.$parent
            }
            let component = null
            // find() 方法返回通过测试(函数内判断)的数组的第一个元素的值。
            // find() 方法为数组中的每个元素都调用一次函数执行:
            // 当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
            // 如果没有符合条件的元素返回 undefined
            // 注意: find() 对于空数组,函数是不会执行的。
            // 注意: find() 并没有改变数组的原始值。
            // const route = this.$router.$options.routes.find(
            //     // 这里不能用this.current,因为this指向当前组件实例
            //     // 由于Vue.mixin全局混入时已挂载router,故可以取出current
            //     (route) => route.path === this.$router.current
            // )
            console.log(this.$router.matcher, depth)
            const route = this.$router.matcher[depth]
            if (route) {
     
                component = route.component
            }
            return h(component)
        }
    })
}

export default VueRoute

五、总结

这是一篇学习笔记,可能有的地方描述的不是很准确,欢迎大家指出和讨论~~!

你可能感兴趣的:(#,造轮子系列,vue,javascript,js)