全网首发:Vue3.5 源码解析, useTemplateRef 实现原理Vue 3.5 源码解析,全网首发,遥遥领先!
Hello,大家好,我是 Sunday。
2024年9月3日,Vue 3.5 的正式版终于来了。
前几天咱们分享了 Vue 3.5 新特性 其中 useTemplateRef
这个 API
被很多同学所关注。那么这个 API 在源码中究竟是怎么实现的呢?今天咱们就来看一下!
useTemplateRef 的作用
useTemplateRef
是用来专门获取 dom 或者 组件示例 的。
在之前,如果我们想要获取 dom ,那么需要这么做:
- 先为 dom 指定 ref 属性,并且给定一个 value 值
- 在 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
源码实现并不复杂,主要逻辑分为两步:
- 通过
Object.defineProperty
监听ref[key]
的setter 行为
,为r.value
赋值 - 通过
shallowRef
生成ref 实例
,并作为useTemplateRef
的返回值
前端训练营:1v1私教,终身辅导计划,帮你拿到满意的
offer
。 已帮助数百位同学拿到了中大厂offer
转载自:https://juejin.cn/post/7410321051782037538