vue3.5中的useTemplateRef

文章类型:Vue

发布者:hp

发布时间:2024-11-18

一:产生原因

vue3访问DOM和子组件使用ref进行模板引用,这就会产生一个问题,到底ref是个响应数据还是DOM元素,

1:定义响应数据

const inputEl =ref(null)

2:传统ref定义在DOM上

<input type="text" ref="inputEl" />

const inputEl = ref<HTMLInputElement>();

进行Hooks进行封装导出,需要将ref中的变量导出才可以使用

export default function useRef() {
  const inputEl = ref<HTMLInputElement>();
  function setInputValue() {
    if (inputEl.value) {
      inputEl.value.value = "Hello";
    }
  }

  return {
    inputEl,
    setInputValue,
  };
}

进行引用,就会导致一个变量引入,但是没有显示使用

const { setInputValue, inputEl } = useInput();

所以,Vue3.5发现了这个问题,新增了useTemplateRef函数

二:使用方式

1:定义:接受一个参数key,是一个字符串,返回一个ref变量

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

导出使用,就不需要再导出变量值

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

三:原理

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;
}

1:使用getCurrentInstance方法获取当前vue实例对象,赋值给变量i

2:调用shallowRef函数生成一个浅层的ref对象,初始值为null。

3:判断当前vue实例如果存在就读取实例上面的refs属性对象,如果实例对象上面没有refs属性,那么就初始化一个空对象到vue实例对象的refs属性

4:使用了Object.defineProperty方法对refs属性对象进行拦截,拦截的字段是变量key的值,而这个key的值就是template中使用ref属性绑定的值

5:最后返回这个ref对象

四:总结

1:useTemplateRef函数主要是更直观的区分ref是DOM还是ref

2:解决定义ref作为DOM元素后,封装成Hooks后导出变量名后必须引入

五:拓展

1:动态绑定ref变量和赋值

 <input type="text" :ref="refKey" />
 const refKey = ref("inputEl1");
const inputEl1 = useTemplateRef<HTMLInputElement>("inputEl1");
const inputEl2 = useTemplateRef<HTMLInputElement>("inputEl2");
function switchRef() {
  refKey.value = refKey.value === "inputEl1" ? "inputEl2" : "inputEl1";
}