Vue3.5 源码解析, useTemplateRef 实现原理

开发 前端
useTemplateRef 的实现并不复杂,本质上 依然是基于 ref 的实现,只不过是在 ref 上进行了封装。访问 vue-next-3.5.0-master/packages/runtime-core/src/helpers/useTemplateRef.ts​ 下的代码,可以看到 useTemplateRef 的实现逻辑。

Hello,大家好,我是 Sunday。

2024年9月3日,Vue 3.5 的正式版终于来了。

前几天咱们分享了 Vue 3.5 新特性 其中 useTemplateRef 这个 API 被很多同学所关注。那么这个 API 在源码中究竟是怎么实现的呢?今天咱们就来看一下!

useTemplateRef 的作用

useTemplateRef 是用来专门获取 dom 或者 组件示例 的。

在之前,如果我们想要获取 dom ,那么需要这么做:

  1. 先为 dom 指定 ref 属性,并且给定一个 value 值
  2. 在 js 中,声明 value 值的变量,并且给定初始值为 空的 ref
<script setup>
// 首先,您定义了一个值为undefined或空的ref
// 并以您想要的方式命名生成的可用内容
const divEl = ref();
</script>

<template>
<!-- 然后使用与“ref”属性的值相同的名称,在模板中的某个地方 -->
<div ref="divEl" ></div>
</template>

但是,这种方案存在一个问题,那就是:ref 通常用来声明响应式数据。当 ref 不光作为响应式声明,还被作为 dom 实例的时候,那么就难免有点让人疑惑了。

所以在(3.5 之后) Vue 推出了一个新的 API 叫做 useTemplateRef 来解决这个问题:

<template>
 <div>
  <div ref="el">程序员Sunday</div>
 </div>
</template>

<script setup>
import { onMounted, useTemplateRef } from 'vue'

const elRef = useTemplateRef('el')

onMounted(() => {
 console.log(elRef.value) // dom 示例
})
</script>

useTemplateRef 的实现原理

useTemplateRef 的实现并不复杂,本质上 依然是基于 ref 的实现,只不过是在 ref 上进行了封装。

访问 vue-next-3.5.0-master/packages/runtime-core/src/helpers/useTemplateRef.ts 下的代码,可以看到 useTemplateRef 的实现逻辑。

图片图片

直接看这个代码是有点复杂的,我们把它简化一下:

export function useTemplateRef(
  key: Keys,
){
  const i = getCurrentInstance()
  const r = shallowRef(null)
  if (i) {
    const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs

    Object.defineProperty(refs, key, {
        enumerable: true,
        get: () => r.value,
        set: val => (r.value = val),
      })
  }
  return r
}

剔除掉 “边缘逻辑” 之后,我们可以得到如上代码。

首先来看 入参:key:

key 代表传入 ref 值,比如在 useTemplateRef('el') 中,代表的就是 "el"

然后是变量,这里主要涉及到两个:

第一个 i:通过 getCurrentInstance() 获取,得到的是 上下文实例。

接下来,通过 i.refs 获取到所有的 ref 数据,然后为 refs 添加 Object.defineProperty 的监听,监听的属性名就是入参 key。如果以 useTemplateRef('el') 为例,那么就是 "el"。

通过监听对应 key 的 get 和 set 标记,这里 重点关注 set 标记,在这里为 r.value 进行了赋值,即:r.value = val。这里的 val 就是 refs[key] 的值,也就是对应的 ref 组件实例。

第二个 r:通过 shallowRef(null) 获取,作为返回值

r 作为 useTemplateRef 的返回值即 最终获取的组件示例。

查看 shallowRef 方法(vue-next-3.5.0-master/packages/reactivity/src/ref.ts),可以看到该方法最终会生成 ref 示例:

图片图片

同时,在上面我们知道了 r.value 的值,是在触发 refs[key] 的 setter 行为时赋值的,赋值的对象即为 ref 组件实例

因此,当 useTemplateRef 返回 r 时,我们就可以通过 r.value 拿到 ref 组件实例 了

总结

那么到这里,我们就看完了 useTemplateRef 的大致源码。整个 useTemplateRef 源码实现并不复杂,主要逻辑分为两步:

  1. 通过 Object.defineProperty 监听 ref[key] 的 setter 行为,为 r.value 赋值
  2. 通过 shallowRef 生成 ref 实例,并作为 useTemplateRef 的返回值
责任编辑:武晓燕 来源: 程序员Sunday
相关推荐

2024-09-05 09:17:14

2024-09-02 08:48:45

2024-11-06 10:47:53

2024-07-11 09:00:13

2024-10-14 12:56:28

2024-01-18 08:31:22

go实现gorm框架

2021-05-26 11:30:24

Java线程池代码

2021-10-27 16:52:37

LayoutInfl源码解析

2021-02-02 13:45:31

Vue代码前端

2017-05-31 13:23:41

神经网络深度学习

2022-12-09 08:10:12

kubectl容器源码

2021-09-09 06:55:43

AndroidViewDragHel原理

2020-10-10 08:20:27

Spring Boot运行原理代码

2020-08-12 11:05:32

Vue 源码应用

2020-06-09 11:35:30

Vue 3响应式前端

2023-05-18 09:00:39

Nuxt类型Nuxt 2

2021-09-27 06:29:47

Vue3 响应式原理Vue应用

2021-08-27 07:47:07

Nacos灰度源码

2013-06-08 10:11:31

Java线程池架构

2015-10-10 09:39:42

Java线程池源码解析
点赞
收藏

51CTO技术栈公众号