vue-router 如何做到页面切换?<root-view>, <root-link> 源码解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 写路由相关的代码的时候,写完页面组件的相关的代码后,就可以直接一把梭哈 vue-router,但是 vue-router 是怎么实现它们间的切换的呢?

写路由相关的代码的时候,写完页面组件的相关的代码后,就可以直接一把梭哈 vue-router,但是 vue-router 是怎么实现它们间的切换的呢?

不知道大家有没有写过 JSP,我曾经短暂的写过一个星期 JSP, 过程让我有点煎熬,JSP 的工程化没有现在的我们使用的 webpack 那么好,就比如热更新这一开,JSP 的更新是需要重启整个 SpringBoot! 很难受,但可以说一下的是 JSP 实现页面切换的功能其实写起来和 vue-router 也是差不多的

<!-- 有点命名视图的感觉了 -->
<jsp:include page="head.jsp"></jsp:include>
<html></html>
<jsp:include page="bottom.jsp"></jsp:include>
<router-view name="head"></router-view>
<router-view></router-view>
<router-view name="bottom"></router-view>

JSP 的页面切换就像你看得一样,点击后返回目录中的 .jsp 文件,那么 vue-router 是怎么去做的呢?

<router-link>

<router-link> 其实没什么好讲的,源码小 300 行,让我们浅尝一下源码,router/src/RouterLink.ts 184

export const RouterLinkImpl = /*#__PURE__*/ defineComponent({
  // ...
  setup(props, { slots }) {
    const link = reactive(useLink(props));
    const { options } = inject(routerKey)!;

    // 样式计算
    // ...

    return () => {
      const children = slots.default && slots.default(link);
      return props.custom
        ? children
        : h(
            "a",
            {
              "aria-current": link.isExactActive
                ? props.ariaCurrentValue
                : null,
              href: link.href,
              // this would override user added attrs but Vue will still add
              // the listener so we end up triggering both
              onClick: link.navigate,
              class: elClass.value,
            },
            children
          );
    };
  },
});

简单来说就是返回一个 <a>, 里面包含你要跳转的路由(即 URL),说过题外话,像这种库一般都是用 render 函数去渲染组件的(就是上面那个 h()),而不是像我们平时用的模板语法 <template>, 什么叫函数式组件?这就叫函数式组件

注意 <router-link> 有个小细节,看上面那个 onClick 就是拦截 <a> 原有的跳转事件,为什么这么做?因为 vue-router4 采用的是 history 模式,不同于 hash 模式 URL 前面挂个 # 就随便你怎么改都不会发送到服务器,history 模式就需要把跳转给拦截不然就跳到 404 了,下面有个例子

https://www.baidu.com/xxx  // 这个访问会报错
https://www.baidu.com/#xxx // 这个就不会

再说一个小细节,vue-router4 是默认拦截的,因为 vue-router4 的 hash 模式不是正经 hash, 其底层也是用的 history 模式

<router-view>

注意注意 <router-view> 才是页面切换的敲门砖,但 <router-view> 的源码比 <router-link> 要少,有 231 行,不过核心都是一样的,router/src/RouterView.ts 43(我们还是挑最核心的部分来看)

export const RouterViewImpl = /*#__PURE__*/ defineComponent({
  setup(props, { attrs, slots }) {
    const injectedRoute = inject(routerViewLocationKey)!
    const routeToDisplay = computed(() => props.route || injectedRoute.value)
    const depth = inject(viewDepthKey, 0)
    // 获得匹配路由
    const matchedRouteRef = computed<RouteLocationMatched | undefined>(
      () => routeToDisplay.value.matched[depth]
    )

    // 传递给子 router-view 它自己的路由层级
    provide(viewDepthKey, depth + 1)
    provide(matchedRouteKey, matchedRouteRef)
    provide(routerViewLocationKey, routeToDisplay)

    return () => {
      const route = routeToDisplay.value
      const matchedRoute = matchedRouteRef.value
      const ViewComponent = matchedRoute && matchedRoute.components[props.name]
      const currentName = props.name

      if (!ViewComponent) {
        return normalizeSlot(slots.default, { Component: ViewComponent, route })
      }

      const component = h(
        ViewComponent,
        assign({}, routeProps, attrs, {
          onVnodeUnmounted,
          ref: viewRef,
        })
      )
    }
  },
})

小细节小细节,<router-view> 深度迎合套娃性质,制定了一套 provide/inject 的父子 <router-view> 传递信息规则,通过父 <router-view> 传递下来的信息,使用 vue-router4 的 matcher 去找到正确的路由组件,没错,所谓的页面切换就是找组件的一个过程,对于一个个分散开来的 vue 组件,通过 matched 去找到组件,没错今天的主角就是 matcher!

又是一个小细节

matcher

matcher 其实是 vue-router 里面的核心,它有一个专门的文件夹 router/src/matcher,通过这个 matcher, vue-router 将从我们每一次路由导航的点击当中找到真正的路由组件

来到 router/src/router.ts 356

export function createRouter(options: RouterOptions): Router {
  const matcher = createRouterMatcher(options.routes, options)
  ...
}

再配合一点官方文档的小知识

const Home = { template: "<div>Home</div>" };
const About = { template: "<div>About</div>" };

// options.routes 就是这里的 routes
const routes = [
  { path: "/", component: Home },
  { path: "/about", component: About },
];

const router = VueRouter.createRouter({
  history: VueRouter.createWebHashHistory(),
  routes,
});

createRouterMatcher() 的目的是为了创建我们定义的路由 routes 对应的映射表,而 <router-view> 就是通过这个映射表找到相应的组件并渲染

总结

vue-router4 页面切换

  1. Matcher 构建映射表
  2. <router-link> 拦截 <a> 跳转, 传递信息到 history 监听器
  3. <router-link> 根据 provide/inject 的信息, 找到映射表中的组件并渲染

这是写的关于 vue-router 的第三篇文章,基本上 vue-router4 的核心源码都看完了,但感觉家人们好像不是很喜欢看源码一类的文章,其实像 vue-router 这种我们写 vue 项目时几乎是必然要用的库,有机会还是可以去看一下的,源码并没有我一开想象的难,尤其 vue-router4 是用 typescript 写的,相较于传统的 JS 写的库,git clone 下来一堆报错,而 vue-router4 就不会,而且 vs-code 还提供了对 typescirpt 的分析功能,很方便就能够找到 vue-router4 中的某个方法的定义,某个变量的声明(当然 vs-code 做得还不够好,改天我会用 webstorm 去看一下),所以看源码难的是开始的决心

相关文章
|
6天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
20 2
|
6天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
19天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
38 3
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
53 5
|
1月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
111 5
|
1月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
1月前
|
JavaScript 调度
Vue事件总线(EventBus)使用指南:详细解析与实战应用
Vue事件总线(EventBus)使用指南:详细解析与实战应用
58 1
|
1月前
|
JavaScript
深入解析:JS与Vue中事件委托(事件代理)的高效实现方法
深入解析:JS与Vue中事件委托(事件代理)的高效实现方法
41 0
|
1月前
|
存储 JavaScript 前端开发
Vue.js项目中全面解析定义全局变量的常用方法与技巧
Vue.js项目中全面解析定义全局变量的常用方法与技巧
41 0
|
1月前
|
缓存 JavaScript API
全面解析 Pinia:Vue 状态管理的新选择
本文深入探讨了 Pinia,作为 Vuex 的替代品,提供了一种更简洁和高效的状态管理方案。文章涵盖了 Pinia 的核心特性,包括支持 Vue2 和 Vue3、TypeScript 支持、无需嵌套模块的设计,以及对同步和异步操作的支持。详细介绍了如何创建和使用 Store,管理状态、Getters 和 Actions,重置状态以及通过 $patch 方法批量更新状态。最后,探讨了如何在不同 Store 之间共享数据和逻辑,为开发者提供了实用的 Pinia 使用指南。
24 0

推荐镜像

更多