详解vue中router-view组件的生成原理

 更新时间:2024年01月19日 08:25:34   作者:唐朝人  
在vue的使用过程中,有一个组件,几乎是必用的,那就是router-view,它是所有组件的入口,是单页面系统的一把利剑,如果你的系统是火箭,那么router-view无疑将是这艘火箭的北斗卫星,本文将给大家介绍vue中的router-view组件是如何生成的,感兴趣的朋友可以参考下

前言

在vue的使用过程中,有一个组件,几乎是必用的,那就是router-view。它是所有组件的入口,是单页面系统的一把利剑。如果你的系统是火箭,那么router-view无疑将是这艘火箭的北斗卫星。

router-view

< router-view />是 vue-router 默认注册的全局组件。如果你从0搭建过系统的话,一定记得在Layout或者AppMain组件中有过这样一行代码:

 <transition name="fade-transform" mode="out-in">
    <div style="height: 100%">
    // 所有组件的入口
      <router-view />
    </div>
</transition>

我们一直在用,但是有没有想过:这个组件是干嘛用的?为什么注册为全局组件,又为什么是所有页面组件的入口?它……究竟有什么用?

带着这样的疑问,我打开了 vue-router 的gitHub切换到 vue2 分支

然后 在 src->components->view.ts 中找到了 router-view 的相关代码:

下面来分析一下这个组件的源码,看一下 vue-router 是怎么设计这个组件的,以及这种设计带给我们的思考有哪些?

为了便于理解,我将这段源码粘贴下来:

// @ts-nocheck

import { Component } from 'vue'

const View: Component = {
  name: 'RouterView',
  functional: true,

  props: {
    name: {
      type: String,
      default: 'default',
    },
  },

  render(_, { children, parent, data, props }) {
    data.routerView = true
    const h = parent.$createElement
    const route = parent.$route

    // determine current view depth, also check to see if the tree
    // has been toggled inactive but kept-alive.
    let depth = 0
    // let inactive = false
    // @ts-ignore
    while (parent && parent._routerRoot !== parent) {
      const vnodeData = parent.$vnode && parent.$vnode.data
      if (vnodeData) {
        // @ts-ignore
        if (vnodeData.routerView) {
          depth++
        }
        // if (vnodeData.keepAlive && parent._inactive) {
        //   inactive = true
        // }
      }
      parent = parent.$parent
    }
    data.routerViewDepth = depth
    const matched = route.matched[depth]
    if (!matched) return h()

    const component = matched.components[props.name]

    return h(component, data, children)
  },
}

export default View
  • 引入依赖

引入 vue 中的 component 组件,然后注册一个 name 为 RouterView 的组件,这里的functional:true 表示这个组件是一个函数式组件。

import { Component } from 'vue'
const View: Component = {
  name: 'RouterView',
  functional: true,

  props: {
    name: {
      type: String,
      default: 'default',
    },
  }

组件内部定义了一个 props,接受参数 name。一般情况下,router-view 很少传 name,所以这个 参数可以忽略

  • render函数的参数通过解构赋值拿到组件内部属性

我们在 render 中拿到了 children、parent、data、props 等属性。在 parent 中我们取到了 $createElement 内部方法,以及 route 路由信息,然后定义了一个 depth 变量,并初始赋值为 0。

render(_, { children, parent, data, props }) {
    data.routerView = true
    const h = parent.$createElement
    const route = parent.$route

    // determine current view depth, also check to see if the tree
    // has been toggled inactive but kept-alive.
    let depth = 0 

这个 depth 意义重大!一开始我也在疑惑,vueRouter 在设计时为什么要有这 个 depth,这个变量有什么用?

后来仔细翻看了代码,发现 depth 记录了每一条路由的索引,然后又将该索引赋值给$vnode->data->routerViewDepth

// has been toggled inactive but kept-alive.
    let depth = 0
    // let inactive = false
    // @ts-ignore
    while (parent && parent._routerRoot !== parent) {
      const vnodeData = parent.$vnode && parent.$vnode.data
      if (vnodeData) {
        // @ts-ignore
        if (vnodeData.routerView) {
          depth++
        }
        // if (vnodeData.keepAlive && parent._inactive) {
        //   inactive = true
        // }
      }
      parent = parent.$parent
    }
    data.routerViewDepth = depth
    const matched = route.matched[depth]

注意看:routerViewDepth 就是这个 depth 值。

可是,routerViewDepth 只是记录了这个值而已,depth 难道没有其他作用了吗?当然不是,如果仅仅是记录一个索引值,但就没必要大动干戈地调用 while 循环深度遍历了。所以,我们继续往下看:

    const matched = route.matched[depth]
    if (!matched) return h()

    const component = matched.components[props.name]

    return h(component, data, children)

在当前路由下,有个 route 对象,里面记录了页面的基本信息。其中有一个 matched 属性至关重要,它是一个 components 集合。也就是说,在当前路由下的所有组件(包括父级 组件以及兄弟组件),都存在于这个 matched 集合里。

而 depth 是当前路由的索引,也就是 matched 集合中的 key。通过 depth 我们能对应到路由中对应好的 component。

所以我们前面为什么说depth 意义重大,原因就在这里。

const matched = route.matched[depth]
    if (!matched) return h()

    const component = matched.components[props.name]

    return h(component, data, children)

最后,源码中增加了一层判断,如果没有找到 matched 则返回一个空的 vnode,如果找到了则返回对应的 component,最后完成渲染。

matched

matched 集合是从路由记录树中根据当前路由的路径生成的。当发生路由导航时,Vue Router 会遍历路由配置,并根据当前路径匹配对应的路由记录。这个过程是自动完成的,无需手动操作。

例如:假设你的路由如下:

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    children:[
       {
        path: '/about',
        name: 'About',
        component: About,
      },
    ]
  },
 
  // ...
];

当访问 /about 路径时,Vue Router 会生成一个 matched数组,其中包含与当前路径 /about匹配的路由记录。在这种情况下,matched数组可能如下所示:

[
  {
    name:'Home',
    alias: xx,
    beforeEnter: (...),
    components: Object,
    enteredCbs: (...),
    instances: (...),
    matchAs: (...),
    meta: (...),
    xxx
  },
  {
    alias: xx,
    name:'About',
    beforeEnter: (...),
    components: Object,
    enteredCbs: (...),
    instances: (...),
    matchAs: (...),
    meta: (...),
    xxx
  },
]

matched数组的顺序是根据路由配置的嵌套关系确定的,父级路由记录在数组中的顺序靠前。

注意,matched数组是在路由导航过程中生成的,因此在路由导航之前或导航到未定义的路径时,matched 数组可能为空。

可以通过在路由组件中访问 this.$route.matched 来获取当前路由的匹配路由记录数组。

总结

虽然现在已经进入了 vue3 的世界,但是 vue2 源码中的诸多设计仍然值得我们借鉴,技术始终服务于业务。相信现在不少公司仍然有大量的 vue2 项目在维护,推翻 shi 山,深度重构不是一朝一夕的事。对于源码,我们可以抱着学习的态度去看待,或者看成是一种兴趣。源码看多了,你的代码不会再 shi,在不知不觉中逐渐向优秀的人靠齐。

以上就是详解vue中的router-view组件是如何生成的的详细内容,更多关于vue router-view组件生成的资料请关注脚本之家其它相关文章!

相关文章

  • VUE实现时间轴播放组件

    VUE实现时间轴播放组件

    这篇文章主要为大家详细介绍了VUE实现时间轴播放组件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • pm2部署vue的实现步骤

    pm2部署vue的实现步骤

    本文主要介绍了使用PM2运行Vue项目的具体步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • 基于vue实现8小时带刻度的时间轴根据当前时间实时定位功能

    基于vue实现8小时带刻度的时间轴根据当前时间实时定位功能

    这篇文章主要介绍了基于vue实现8小时带刻度的时间轴根据当前时间实时定位功能,开始时间、结束时间可配置,根据当前时间初始化位置,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • vue+mock.js实现前后端分离

    vue+mock.js实现前后端分离

    这篇文章主要为大家详细介绍了vue+mock.js实现前后端分离,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • Vue2.0 axios前后端登陆拦截器(实例讲解)

    Vue2.0 axios前后端登陆拦截器(实例讲解)

    下面小编就为大家带来一篇Vue2.0 axios前后端登陆拦截器(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Vue实现input宽度随文字长度自适应操作

    Vue实现input宽度随文字长度自适应操作

    这篇文章主要介绍了Vue实现input宽度随文字长度自适应操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • Element UI表单验证规则动态失效问题的解决办法

    Element UI表单验证规则动态失效问题的解决办法

    这篇文章主要给大家介绍了关于Element UI表单验证规则动态失效问题的解决办法,Element UI提供了强大的表单验证功能,可以轻松地对表单进行验证,需要的朋友可以参考下
    2023-09-09
  • Vue实现英文字母大小写在线转换功能

    Vue实现英文字母大小写在线转换功能

    在Web开发中,字符串处理是常见的需求之一,特别是在国际化应用中,对于文本的格式化处理尤为重要,本文将介绍如何使用Vue.js来构建一个简单的在线英文字母大小写转换工具,需要的朋友可以参考下
    2024-09-09
  • vue项目中按钮防抖处理实现过程

    vue项目中按钮防抖处理实现过程

    这篇文章主要给大家介绍了关于vue项目中按钮防抖处理实现的相关资料,在项目开发中相必大家时常会遇到按钮重复点击后引起事件重复提交的问题,需要的朋友可以参考下
    2023-08-08
  • Vue如何使用百度地图自定义信息窗口InfoWindow的样式

    Vue如何使用百度地图自定义信息窗口InfoWindow的样式

    这篇文章主要介绍了Vue如何使用百度地图自定义信息窗口InfoWindow的样式问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03

最新评论