简单易懂,超详细 Vue-Router 原理实现

Vue-Router 实现思路

  • 创建VueRouter插件,实现静态方法install。为什么要install静态方法呢?因为我们在Vue中注册VueRouter插件,即Vue.use(VueRouter) 时,Vue内部先判断传进来的VueRouter是函数还是对象,函数则执行,对象则调用其install方法,并会将Vue对象作为参数传入。
    • 判断插件是否已经被加载。(只要加载一次)
    • 当Vue加载的时候把传入的router对象挂载到Vue实例上。(只执行一次)
  • 当我们将路由信息放入VueRouter实例化时,即
    const router = new VueRouter({ routes })
    需要在VueRouter插件内部初始化routeMap,初始化组件router-link router-view等工作
    • initRouteMap() 遍历所有路由信息,把组件和路由的映射记录到 routeMap 对象中
    • initComponent() 通过Vue.component创建router-link和router-view组件
    • initEvent() 注册 popstate 事件,当路由地址发生变化,重新记录当前的路径

Vue-Router 代码实现

let _Vue = null

class VueRouter {
    static install(Vue) {
        //1.判断插件是否已经被加载
        if (VueRouter.install._installed) return
        VueRouter.install._installed = true

        //2.将Vue注册到全局
        _Vue = Vue

        //3.当Vue加载的时候,把router对象挂载到Vue实例上
        Vue.mixin({
            beforeCreate() {
                //判断router是否已经挂载到Vue实例上
                if (this.$options.router) {
                    _Vue.prototype.$router = this.$options.router
                }
            }
        })
    }

    constructor(options) {
        this.options = options
        // 记录路由配置中路径和组件 键值对
        this.routeMap = {}
        // 通过Vue的observable将当前路由地址变为响应式
        this.data = _Vue.observable({
            current: '/'
        })
        // 初始化
        this.init()
    }

    init() {
        // 初始化routeMap
        this.initRouteMap()
        // 初始化组件router-link router-view
        this.initComponent()
        // 初始化popstate
        this.initEvent()
    }

    initRouteMap() {
        if (this.options.routes) {
            this.options.routes.forEach(route => {
                this.routeMap[route.path] = route.component
            })
        }
    }

    initComponent() {
        // router-link
        _Vue.component('router-link', {
            props: {
                to: String
            },
            // template: "<a :href='to'><slot></slot></a>"

            render(h) {
                return h('a', {
                    attrs: {
                        href: this.to
                    },
                    on: {
                        click: this.clickHandle
                    }
                }, [this.$slots.default])
            },
            methods: {
                clickHandle(e) {
                    history.pushState({}, "", this.to)
                    this.$router.data.current = this.to
                    e.preventDefault()

                }
            }
        })

        // router-view
        const self = this
        _Vue.component('router-view', {
            render(h) {
                const comp = self.routeMap[self.data.current]
                return h(comp)
            }
        })
    }

    initEvent() {
        window.addEventListener("popstate", () => {
            this.data.current = location.pathname
        })
    }
}

export default VueRouter
  • 注意
    • 创建router-link和router-view组件时,用到了render函数,而不是temlate模板,是因为我们的Vue是运行时版本,无法使用template模板。如果要使用,需要将Vue改为完整版(会比原来大10kb左右)。开启方式:根目录下创建vue.config.js文件,配置如下:
      module.exports = {
          runtimeCompiler: true
      }
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值