vue-router 如何找到待渲染的 vue 组件?vue-router Matcher 解析(一)

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 在之前的一篇文章中曾经说过,页面的切换其实就是一个找组件的过程,也就是 vue-router 里面的 Matcher, 看过 vue-router4 的源码后,发现 matcher 其实是非常重要的一

背景介绍

在之前的一篇文章中vue-router 如何做到页面切换?, 源码解析曾经说过,页面的切换其实就是一个找组件的过程,也就是 vue-router 里面的 Matcher, 看过 vue-router4 的源码后,发现 matcher 其实是非常重要的一环,可以说是和 history(历史模式) 构成了 vue-router 的两个核心,回过头来看一下 vue-router4 中的创建 router 操作

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

其实也暗示着 history(对应历史模式) 和 routes(对应 matcher) 是很重要的,话不多说,本篇文章将会介绍 vue-router4 里面的 matcher

注:本篇文章关于 matcher 的解析和源码均对应 vue-router4 即 vue3 版本的 router

注意区分文章中的, routes 和 route, route 指 { path: "/", Redirect: "/test" }, routes 指 [route,route,...]

addRoute

当我们创建 router 实例时,会从 createRouter 将 options.routers 携带到 createRouterMatcher 并在其中初始化 matcher,代码如下

// code
const routes = [
  { path: "/", component: Home },
  { path: "/about", component: About },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});
// source code
function createRouter(options: RouterOptions): Router {
  // options.routes 即 routes = [{ path: "/",... },{ path: "/about",...}]
  const matcher = createRouterMatcher(options.routes, options);
  // ...
}

重点在 createRouterMatcher 里面的 addRoute,注意是一条条 route 处理的,整个 matcher 中的封装操作将在里面完成,而 route 的解析将告一段落,至于封装的意思就是,刚开始传进去的 routes 将不会是 vue-router4 中真正意义上使用的 routes,matcher 部分会将其进一步处理,像别名这些都要进一步处理

function createRouterMatcher(
  routes: RouteRecordRaw[],
  globalOptions: PathParserOptions
): RouterMatcher {
  // ...
  // 添加初始化后的 routes, 或者说封装后的 routes
  routes.forEach((route) => addRoute(route));
  // ...
}

addRoute 其实在 createRouterMatcher 里面做了下面 5 件事

  1. 初步修缮 routes - normalize
  2. 合并 option
  3. 处理别名 - alia
  4. 第二次修缮 routes
  5. 返回 真 - routes

接下来就针对以上几点去介绍

初步修缮 routes - normalize

这个地方主要是负责路由组件传参这块的内容,具体功能可以看 vue-router4 的文档,而文章这里将会介绍它是怎么做的,为什么要这么做

matcher 在 normalize 这里入口是一个工厂函数(小细节了啊),但是我们要知道 vue-router4 是用 typescript 写的,它的返回值也是有定义的,让我们看看这个函数

function normalizeRouteRecord(record: RouteRecordRaw): RouteRecordNormalized {
  return {
    path: record.path,
    redirect: record.redirect,
    // ...
  };
}

它返回值是一个叫 RouteRecordNormalized 的接口 interface, 它的这个工厂函数里面就是返回一个键与 RouteRecordNormalized 相对应的一个对象,不知道这么做是不是有让读者更方便去看给 RouteRecordNormalized 中的哪个属性如何赋值的意思,像在 java 里面很有可能就是 RouteRecordNormalized(path, redirect, ...) 构造函数创建了,当然前提是 RouteRecordNormalized 得是个 Class(不知道用 ts 的人用 Class 的多不多?)

那么这个 RouteRecordNormalized 的目的就是返回处理后的 copy 值(又是 JS 老生常谈的对象引用值问题),这个时候我们原先设定好的 route 在这里就进行了第一步的处理

// code
const routes = [{ path: "/", component: Home }];
// source code
// route: RouteRecordRaw to route: RouteRecordNormalized

那么这个工厂函数它对我们一开始传进来的 route 做了什么处理呢?接下来就是处理过程

处理过程

上面这两个类型在 vue-router4 的 API 参考文档里面有,处理过程就是对 RouteRecordRaw to RouteRecordNormalized 赋初值以及新增属性,下面是处理前后的 route 比较

// code
const route = { path: "/", component: Home };
// 对应下面的
const route = {
  path: "/",
  redirect: undefined,
  name: undefined,
  meta: undefined,
  alias: undefined,
  beforeEnter: undefined,
  props: undefined,
  children: undefined,
};
// source code: normalizeRouteRecord 处理后
const route = {
  path: "/",
  redirect: undefined,
  name: undefined,
  meta: {},
  beforeEnter: undefined,
  props: undefined,
  children: [],
  // 新增部分
  aliasOf: undefined,
  instances: {},
  leaveGuards: new Set(),
  updateGuards: new Set(),
  enterCallbacks: {},
  components: undefined,
};

这个处理过程比较重要的一个点就是 props 的处理,这里可以看一下 vue-router-API-props 参考, 文档里面的要求可以在源码这里体现出来,简简单单来欣赏一下这段源码

function normalizeRecordProps(
  record: RouteRecordRaw
): Record<string, _RouteRecordProps> {
  const propsObject = {} as Record<string, _RouteRecordProps>;
  // 重定向的路由不会有 props, 因此其 props 值应为 false
  const props =
    (record as Exclude<RouteRecordRaw, RouteRecordRedirect>).props || false;
  if ("component" in record) {
    propsObject.default = props;
  } else {
    // 小细节: props 函数模式能应用到对应的命名视图组件里面
    for (const name in record.components)
      propsObject[name] = typeof props === "boolean" ? props : props[name];
  }

  return propsObject;
}

上面这段源码其实就是针对了 props 的三种模式

  1. 布尔模式
  2. 对象模式
  3. 函数模式

在 props 的获取中,对于重定向路由做了特殊处理,默认设置为 false, 但是如果手贱设置了 props 属性,不知道会不会对 vue-router 造成很大的影响?毕竟 vue-router4 其实对 route 的值的约束其实很小

route 指 { path: "/", Redirect: "/test" }

// code
{ path: "/", Redirect: "/test" }
// source code
{ path: "/", Redirect: "/test", props: false }

对象模式和布尔模式是这样处理的

if ('component' in record) {
  propsObject.default = props

这里我们要注意一个小细节, 是设定的 propsObject.default = props, 为什么是 default ? 因为要兼容命名视图,所以无论你一开始的 route 是否是命名视图模式,处理过后的 props 一定有 propsObject.default = xxx

又是一个小细节

命名视图就不用说了,看一下处理后会是怎么样

for (const name in record.components)
  propsObject[name] = typeof props === "boolean" ? props : props[name];
// code
const props = { default: true, other: true }
// source code
const propsObject = { default: true, other: true }

vue-router 文档中没有的小细节

从上面源码中其实我们可以看到,当 route 是命名视图模式时,如果 props 是 boolean 实际上会被应用到每个组件上,比如下面的例子

const routes = [
  {
    path: '/user/:id',
    components: { default: User, sidebar: Sidebar },
    props: true
  }
]
// 等于下面这个
const routes = [
  {
    path: '/user/:id',
    components: { default: User, sidebar: Sidebar },
    props: { default: true, sidebar: true }
  }
]

总结

vue-router 的 matcher

  1. createRouterMatcher - 创建 matcher 并初始化
  2. addRoute - 初始化传入的 route
  3. normalizeRouteRecord - 封装传入的 route, 相当于升级,对应 API 文档的 RouteRecordNormalized
  4. normalizeRecordProps - 处理 props,对应文档的路由传参部分

结尾也回归主题一下吧,想要找到组件那你找不就完了?那如果你的目的是这个,那看到 addRoute 那里你就可以结束 matcher 的部分了,那 addRoute 了,内存就有一个路由映射表了,剩下的就找不就完事了?但文章其实要讲的更加细节(没错,是细节怪)找到正确的路由才算成功,那路由的处理也算是一部分

vue-router matcher 部分的 normalize 就先讲到这里,讲了 matcher 对 route 的第一步处理,如果你对 vue-router4 的源码感兴趣的话,觉得文章讲的还行,可以看之后发的几篇源码解析(关注就能看到后续更新)

  1. vue-router 如何找到待渲染的 vue 组件?vue-router Matcher 解析(二)- 没写 - 处理别名
  2. vue-router 如何找到待渲染的 vue 组件?vue-router Matcher 解析(三)- 没写 - 第二次处理 routes
相关文章
|
14天前
|
缓存 JavaScript UED
Vue 的动态组件与 keep-alive
【10月更文挑战第19天】总的来说,动态组件和 `keep-alive` 是 Vue.js 中非常实用的特性,它们为我们提供了更灵活和高效的组件管理方式,使我们能够更好地构建复杂的应用界面。深入理解和掌握它们,以便在实际开发中能够充分发挥它们的优势,提升我们的开发效率和应用性能。
38 18
|
9天前
|
缓存 JavaScript UED
Vue 中实现组件的懒加载
【10月更文挑战第23天】组件的懒加载是 Vue 应用中提高性能的重要手段之一。通过合理运用动态导入、路由配置等方式,可以实现组件的按需加载,减少资源浪费,提高应用的响应速度和用户体验。在实际应用中,需要根据具体情况选择合适的懒加载方式,并结合性能优化的其他措施,以打造更高效、更优质的 Vue 应用。
|
10天前
|
监控 JavaScript 前端开发
Vue 异步渲染
【10月更文挑战第23天】Vue 异步渲染是提高应用性能和用户体验的重要手段。通过理解异步渲染的原理和优化策略,我们可以更好地利用 Vue 的优势,开发出高效、流畅的前端应用。同时,在实际开发中,要注意数据一致性、性能监控和调试等问题,确保应用的稳定性和可靠性。
|
10天前
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
19 1
|
13天前
|
前端开发 UED
vue3知识点:Suspense组件
vue3知识点:Suspense组件
27 4
|
12天前
|
JavaScript 前端开发 测试技术
组件化开发:创建可重用的Vue组件
【10月更文挑战第21天】组件化开发:创建可重用的Vue组件
22 1
|
13天前
|
JavaScript 前端开发 Java
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
25 2
|
13天前
|
Java
vue3知识点:Teleport组件
vue3知识点:Teleport组件
24 1
|
14天前
|
JavaScript 前端开发
vue全局公共组件自动引入并注册,开发效率直接起飞!
【10月更文挑战第14天】vue全局公共组件自动引入并注册,开发效率直接起飞!
37 1
|
5天前
|
机器学习/深度学习 自然语言处理 数据管理
GraphRAG核心组件解析:图结构与检索增强生成
【10月更文挑战第28天】在当今数据科学领域,自然语言处理(NLP)和图数据管理技术的发展日新月异。GraphRAG(Graph Retrieval-Augmented Generation)作为一种结合了图结构和检索增强生成的创新方法,已经在多个应用场景中展现出巨大的潜力。作为一名数据科学家,我对GraphRAG的核心组件进行了深入研究,并在此分享我的理解和实践经验。
20 0

推荐镜像

更多