查看原文
科技

全网首发:Vue3.5 源码解析, useTemplateRef 实现原理

程序员Sunday 程序员Sunday
2024-09-04

前端训练营:1v1私教,终身辅导计划,帮你拿到满意的 offer 已帮助数百位同学拿到了中大厂 offer

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, {
        enumerabletrue,
        get() => r.value,
        setval => (r.value = val),
      })
  }
  return r
}

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

首先来看 入参:key

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

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

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

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

通过监听对应 keygetset 标记,这里 重点关注 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 的返回值

1v1私教,帮大家拿到满意的 offer

  1v1 帮大家 offer 

通,备注【训练营】


继续滑动看下一个
程序员Sunday
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存