Vue2手捏router(嵌套路由版本)

文章详细描述了如何在Vue项目中配置路由表,使用VueRouter处理不同路由模式(hash和history),以及router-link和router-view组件的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、首先配置路由表(router/index.js):

import Vue from 'vue';
import VueRouter from '../plugins/router'


Vue.use(VueRouter)

const router = new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/home',
            component: () => import('../components/Home')
        },
        {
            path: '/course',
            component: () => import('../components/Course'),
            children: [
                {
                    path: 'vue',
                    component: () => import('../components/Vue')
                },
                {
                    path: 'react',
                    component: () => import('../components/React')
                }
            ]
        }
    ]
})


export default router;

二、手捏router(plugins/router.js):

  1. 定义一个响应式的数据列表 matched , 用来存储 路径 ['/home','/course','vue','react']
  2. 通过初始化init函数,获取到当前的路由模式,并进行监听(获取到current值)
  3. 调用match函数,将路由表进行递归处理,判断路由路径是否被当前current包含,包含的话,那么就存储在matched列表中,再次判断是否存在children属性,存在的话,那么就证明是嵌套路由,那么再次执行该函数,matched()进行处理。
import routerLink from '@/plugins/components/routerLink'
import routerView from '@/plugins/components/routerView';

let Vue;

class VueRouter {
    constructor(options) {
        // 保存选项
        this.options = options;
        // 定义一个响应式的变量 url, 保存当前的 hash 值
        this.current = location.hash.slice(1,) || '/';
        // defineReactive 定义响应式的对象
        Vue.util.defineReactive(this, 'matched', [])

        // 初始化 根据路由模式 切换不同的监听事件
        this.init();
    }

    init() {
        // 判断当前的路由模式
        if (this.options.mode === 'hash') {
            addEventListener('hashchange', this.changeHash.bind(this))
        } else if (this.options.mode === 'history') {
            addEventListener('popstate', this.changeHistory.bind(this))
        }

        this.match()
    }


    // hash 路由模式  进行切换 时触发  设置当前的current 为当前 路径
    changeHash() {
        this.current = location.hash.slice(1,)
        this.matched = []
        this.match()
    }

    // history 路由模式 进行路由切换时触发 准确来说时 进行左右箭头切换时触发
    changeHistory() {
        this.current = location.pathname;
        this.matched = []
        this.match()
    }


    match(routes) {
        routes = routes || this.options.routes;

        // 循环routes
        routes.forEach(route=> {
            if( route.path === '/' && this.current.includes(route.path) ) {
                this.matched.push(route)
                
            }
            if( route.path !== '/' && this.current.includes(route.path)) {
                this.matched.push(route);
                if(route.children) {
                    this.match(route.children)
                }
            }
        })
    }
    

}


VueRouter.install = function (_Vue) {
    // 1. 保存 Vue
    Vue = _Vue;
    Vue.mixin({
        beforeCreate() {
            if (this.$options.router) {
                Vue.prototype.$router = this.$options.router
            }
        }
    })

    // 创建 router-link
    Vue.component('router-link', routerLink)

    // 创建router-view组件
    Vue.component('router-view', routerView)
}


export default VueRouter;

2.1 router-link组件:

  1. 通过router.options.mode 来判断当前的路由模式,来进行路由跳转。
  2. hash模式:直接返回 h('a', { attrs: { href: '#' + this.to } }, this.$slots.default) 即可,第一个参数是标签名,第二个参数是元素的属性,第三个参数是元素的内容。
  3. history模式:他在hash模式的基础上,需要添加一个on事件,就是点击事件,通过history.pushState来进行路由的跳转,将当前的current设置为跳转的目的地。通过调用this.$router.changeHistory()方法重新调用this.matched()方法获取到路由表映射。
export default {
    props: {
        // 接收参数 去向
        to: {
            type: String || Object,
            required: true,
        }
    },
    // h 有三个参数,第一个参数创建的元素,第二个参数元素具有的属性, 第三个参数元素的内容
    render(h) {
        const router = this.$router;
        if( router.options.mode === 'hash' ) {
            return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default)
        } else if( router.options.mode === 'history' ) {
            return h(
                'a',
                {
                    attrs: {
                        href: this.to
                    },
                    // history 进行跳转时需要自己手动进行点击事件
                    on: {
                        'click': ev => {
                        // 1. 阻止a链接的默认跳转行为
                        ev.preventDefault();
                        // 2. 调用pushState方法来实现跳转: pushState(obj, title, url)
                        history.pushState({}, '', this.to);
                        // 3. 设置 current 的值
                        router.current = this.to;
                        // 4.点击的时候再次调用changeHistory方法
                        this.$router.changeHistory()
                        }
                    }
                },
                this.$slots.default
            )
        }
    }

}

2.2 router-view组件:

  1. routerView实现思路是通过判断虚拟dom上是否存在数据,存在的话,那么当前的层级就+1,没有的话,那么该层级为0,我们最后可以根据层级在this.matched中获取到当前的路径、对应的组件,进行渲染。
  2. 通过this.$vnode.data.routerView = true; 在当前虚拟dom上标记为true(留个记号)
  3. this.$parent获取到当前的父节点
  4. 通过while进行循环,判断vnodeData是否存在,并且上面存在routerView(咱们设置好的记号),第一层路由触发时,设置这层的虚拟dom为true,父节点的虚拟dom上没有参数,那么就不给depath++,那么为0;当是第二层路由触发时,那么他的父节点会有参数,那么就会进入到判断中,进行depath++,那么为1,在向上找,父节点没有虚拟dom,那么直接返回,最后depath为1。
  5. 通过matched列表使用depath来获取当前路由信息(路径和component)
export default {
    name: 'routeView',
     // 组件要重新渲染,必须要让数据发生改变的时候,重新去执行render函数
     render(h) {
        // 标记一下当前的组价是routeView组件
        this.$vnode.data.routerView = true;
        // 保存当前路由的层级
        let depath = 0;
        // 当前routeView组件的父节点
        let parent = this.$parent;

        while(parent) {
            const vnodeData = parent.$vnode && parent.$vnode.data;
            // 证明我当前找到的这个节点就是routeView组件
            if(vnodeData && vnodeData.routerView) {
                depath++;
            }
            // 不断向上寻找,查找到父节点,直到没有父节点了,退出循环。
            parent = parent.$parent;
        }


        const router = this.$router;
        let component = null;
        let route = router.matched[depath];
        if(route) {
            component = route.component;
        }

        return h(component)
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
OSZAR »