Vue 3.5 正式版来了!源码解析 useTemplateRef 实现原理

2024-09-0411:41:00WEB前端开发Comments195 views字数 2039阅读模式

2024年9月3日,Vue 3.5 的正式版终于来了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

前几天咱们分享了 Vue 3.5 新特性 其中 useTemplateRef 这个 API 被很多同学所关注。那么这个 API 在源码中究竟是怎么实现的呢?今天咱们就来看一下!文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

useTemplateRef 的作用

useTemplateRef 是用来专门获取 dom 或者 组件示例 的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

在之前,如果我们想要获取 dom ,那么需要这么做:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

  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 实例的时候,那么就难免有点让人疑惑了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

所以在(3.5 之后) Vue 推出了一个新的 API 叫做 useTemplateRef 来解决这个问题:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

<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 上进行了封装文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

访问 vue-next-3.5.0-master/packages/runtime-core/src/helpers/useTemplateRef.ts 下的代码,可以看到 useTemplateRef 的实现逻辑文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

Vue 3.5 正式版来了!源码解析 useTemplateRef 实现原理

直接看这个代码是有点复杂的,我们把它简化一下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

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
}

剔除掉 “边缘逻辑” 之后,我们可以得到如上代码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

首先来看 入参:key

key 代表传入 ref 值,比如在 useTemplateRef('el') 中,代表的就是 "el"文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

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

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

接下来,通过 i.refs 获取到所有的 ref 数据,然后为 refs 添加 Object.defineProperty 的监听,监听的属性名就是入参 key。如果以 useTemplateRef('el') 为例,那么就是 "el"文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

通过监听对应 keygetset 标记,这里 重点关注 set 标记,在这里为 r.value 进行了赋值,即:r.value = val。这里的 val 就是 refs[key] 的值,也就是对应的 ref 组件实例文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

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

r 作为 useTemplateRef 的返回值即 最终获取的组件示例文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

查看 shallowRef 方法(vue-next-3.5.0-master/packages/reactivity/src/ref.ts),可以看到该方法最终会生成 ref 示例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

Vue 3.5 正式版来了!源码解析 useTemplateRef 实现原理

同时,在上面我们知道了 r.value 的值,是在触发 refs[key]setter 行为时赋值的,赋值的对象即为 ref 组件实例文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

因此,当 useTemplateRef 返回 r 时,我们就可以通过 r.value 拿到 ref 组件实例文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

总结

那么到这里,我们就看完了 useTemplateRef 的大致源码。整个 useTemplateRef 源码实现并不复杂,主要逻辑分为两步:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

  1. 通过 Object.defineProperty 监听 ref[key]setter 行为,为 r.value 赋值
  2. 通过 shallowRef 生成 ref 实例,并作为 useTemplateRef 的返回值

程序员Sunday文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/65067.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/65067.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定