综合 2024年Vue3.5的useTemplateRef让ref操作DOM更加丝滑

2024-11-19 06:37:21 +0800 CST views 1146

牛逼!Vue3.5的useTemplateRef让ref操作DOM更加丝滑

前言

在 Vue 3 中,访问 DOM 和子组件可以通过 ref 进行模板引用,但这个 ref 有时会让人迷惑。比如,定义的 ref 变量到底是一个响应式数据还是 DOM 元素?为什么 templateref 属性的值是一个字符串(例如 ref="inputEl"),却能和 script 中同名的 inputEl 变量绑定在一起?为了解决这些问题,Vue 3.5 推出了 useTemplateRef 函数,使得 ref 的使用更加符合编程直觉。

ref 模版引用的问题

我们先来看一下在 React 中使用 ref 访问 DOM 元素的例子:

const inputEl = useRef<HTMLInputElement>(null);
<input type="text" ref={inputEl} />

useRef 返回的对象是一个 .current 属性,inputEl 通过 ref 属性绑定到 input 元素后,.current 的值就被更新为该 input 元素。这个行为符合编程直觉。

然而,在 Vue 3 中,使用 ref 时的行为却没有这么直观。例如,以下代码中,我们试图像 React 那样在 template 中给 ref 属性绑定一个 ref 变量:

<input type="text" :ref="inputEl" />
const inputEl = ref<HTMLInputElement>();

这么写不但不会报错,但访问 inputEl 时总是 undefined。原因是 ref 属性接受的并不是 ref 变量,而是其名称。正确的代码应该是这样的:

<input type="text" ref="inputEl" />
const inputEl = ref<HTMLInputElement>();

如果我们将 ref 模版引用的逻辑抽取为 hooks,情况更加复杂。即使 Vue 组件中不使用该变量,我们仍需要在组件中导入该 ref 变量。这看起来很奇怪。

useTemplateRef 函数

为了解决这些问题,Vue 3.5 引入了 useTemplateRef 函数。这个函数的用法非常简单:它接收一个参数 key(字符串),返回一个 ref 变量。这个 key 值与 templateref 属性的值相同。返回的 ref 变量指向 DOM 元素或子组件。

示例代码如下:

<template>
  <input type="text" ref="inputRef" />
  <button @click="setInputValue">给 input 赋值</button>
</template>

<script setup lang="ts">
import { useTemplateRef } from "vue";

const inputEl = useTemplateRef<HTMLInputElement>("inputRef");
function setInputValue() {
  if (inputEl.value) {
    inputEl.value.value = "Hello, world!";
  }
}
</script>

这样,inputEl 作为一个 ref 变量,可以通过 useTemplateRef 函数获取到 input 元素。相比之前的做法,这种方式更符合编程直觉。

hooks 中使用 useTemplateRef

对于 hooks 中的例子,我们可以用 useTemplateRef 来简化代码:

export default function useInput(key) {
  const inputEl = useTemplateRef<HTMLInputElement>(key);
  function setInputValue() {
    if (inputEl.value) {
      inputEl.value.value = "Hello, world!";
    }
  }
  return {
    setInputValue,
  };
}

组件中的代码如下:

<template>
  <input type="text" ref="inputRef" />
  <button @click="setInputValue">给 input 赋值</button>
</template>

<script setup lang="ts">
import useInput from "./useInput";
const { setInputValue } = useInput("inputRef");
</script>

使用 useTemplateRef 后,我们不再需要在组件中导入 inputEl 变量。

动态切换 ref 绑定的变量

有时我们需要根据场景动态切换 ref 绑定的变量,这时 ref 属性的值是动态的。useTemplateRef 同样支持这种场景:

<template>
  <input type="text" :ref="refKey" />
  <button @click="switchRef">切换 ref 绑定的变量</button>
  <button @click="setInputValue">给 input 赋值</button>
</template>

<script setup lang="ts">
import { useTemplateRef, ref } from "vue";

const refKey = ref("inputEl1");
const inputEl1 = useTemplateRef<HTMLInputElement>("inputEl1");
const inputEl2 = useTemplateRef<HTMLInputElement>("inputEl2");
function switchRef() {
  refKey.value = refKey.value === "inputEl1" ? "inputEl2" : "inputEl1";
}
function setInputValue() {
  const curEl = refKey.value === "inputEl1" ? inputEl1 : inputEl2;
  if (curEl.value) {
    curEl.value.value = "Hello, world!";
  }
}
</script>

通过动态切换 refKey,可以改变绑定的 ref 变量。

useTemplateRef 的实现

useTemplateRef 的实现实际上很简单:

function useTemplateRef(key) {
  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;
}

在初始化时,template 中处理 ref 属性时会对 Vue 实例上的 refs 属性进行写操作,并触发 set 拦截器,将 ref 变量的值更新为 DOM 元素或组件实例。

总结

Vue 3.5 中新增的 useTemplateRef 函数解决了使用 ref 时的一些直觉问题,使得模板中的 ref 属性与 script 中的 ref 变量绑定更加合理。此外,它使得 hooks 中的逻辑更加清晰,减少了不必要的变量导入。通过 useTemplateRef,我们可以更方便地在模板中获取 DOM 元素或子组件。

复制全文 生成海报 Vue 前端开发 JavaScript

推荐文章

一键配置本地yum源
2024-11-18 14:45:15 +0800 CST
html一份退出酒场的告知书
2024-11-18 18:14:45 +0800 CST
Vue3中的自定义指令有哪些变化?
2024-11-18 07:48:06 +0800 CST
前端如何优化资源加载
2024-11-18 13:35:45 +0800 CST
Python 获取网络时间和本地时间
2024-11-18 21:53:35 +0800 CST
全栈工程师的技术栈
2024-11-19 10:13:20 +0800 CST
npm速度过慢的解决办法
2024-11-19 10:10:39 +0800 CST
Golang在整洁架构中优雅使用事务
2024-11-18 19:26:04 +0800 CST
JavaScript设计模式:发布订阅模式
2024-11-18 01:52:39 +0800 CST
MyLib5,一个Python中非常有用的库
2024-11-18 12:50:13 +0800 CST
为什么大厂也无法避免写出Bug?
2024-11-19 10:03:23 +0800 CST
CSS实现亚克力和磨砂玻璃效果
2024-11-18 01:21:20 +0800 CST
如何在Vue 3中使用Ref访问DOM元素
2024-11-17 04:22:38 +0800 CST
程序员茄子在线接单