全网首发:Vue3.5 源码解析, useTemplateRef 实现原理
前端训练营:1v1私教,终身辅导计划,帮你拿到满意的
offer
。 已帮助数百位同学拿到了中大厂offer
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
我目前在做一个 前端训练营 ,主打的就是:1v1 私教,帮大家拿到满意的 offer
。
也可以直接加我微信沟通,备注【训练营】: