”ref<HTMLElement>() ”写法问题,请教大佬?

const nav = ref<HTMLElement>()
const nav = ref<HTMLElement|null>(null)
const nav = ref(null)
const nav = ref('')

在nav标签上加ref的话,这几种定义写法有啥区别?都可以吗?※现在用的是第二种写法

我在使用这个nav时候,ts提示我
navWidth = nav.value.clientWidth
这样的写法不对,应该是
navWidth = nav.value?.clientWidth
但是在使用navWidth这个变量时候,navWidth可能为定义。这种情况该怎么写好点呢?
必须这样加if判断条件吗
if(nav.value){}

问题2
假如我想定义一个div的关联ref,那我用HTMLElementHTMLDivElement都可以是吧?有啥好的规约吗?像 nav这种标签是没有HTMLNavHTML这样的接口,这种时候怎么办嘞?既然都是HTMLElement的子类,为啥要HTMLDivElement呢,每次在定义时候都用HTMLElement

阅读 4.4k
4 个回答

既然你是用在 TS 里的,后两种写法等于直接丧失了类型信息,直接排除。

其次直接 ref<HTMLElement>() 就好,用不着声明成 ref<HTMLElement | null>

因为 ref() 这个方法的函数签名本身就已经是 function ref<T = any>(): Ref<T | undefined> 了,Vue 已经帮你做好了值可能是未定义的处理了,用不着你多此一举声明出来。

// 没有初始值,此时 a 的类型为 Ref<number | undefined>
let a = ref<number>(); 
// 有初始值,此时 b 的类型为 Ref<number>
let b = ref<number>(1);

我理解为你这个 ref 是要用在 template 里绑定到一个元素或组件的,那么它在 mount 之前确实会是 undefined 的。但如果你确认你的调用一定是在 mount 之后、并且这个元素本身也不存在 v-if 之类的会导致它有可能被销毁,那么直接用非空断言符 ! 即可:

nav.value!.balabala

只用你不能确定的情况下,才需要判断一下是否非空。


HTMLDivElementHTMLElement 多了一个 align 属性。如果你并非用到某个特定元素类型的特有成员(常见于 HTMLImageElementHTMLVideoElement 等),一律声明成 HTMLElement 即可,可以减轻心智负担。

首先,我们来看一下这几种写法的区别:

const nav = ref<HTMLElement>(): 此写法创建了一个 ref,期望它的值为 HTMLElement 类型。但是,这种写法会引发一个错误,因为 TypeScript 需要一个初始值,而此处未提供初始值。

const nav = ref<HTMLElement|null>(null): 此写法创建了一个 ref,期望它的值为 HTMLElement 类型或 null。初始值为 null。这是一个合适的写法,因为在组件挂载之前,ref 可能是 null。

const nav = ref(null): 此写法创建了一个 ref,初始值为 null,但未明确指定类型。TypeScript 可能会推断出类型 Ref<null>,这可能会导致后续使用时的类型错误。

const nav = ref(''): 此写法创建了一个 ref,初始值为空字符串。然而,此写法并不正确,因为期望的值应该是 HTMLElement 类型或 null,而非字符串类型。

建议使用第二种写法。

关于 navWidth 的问题,您可以使用 TypeScript 的非空断言操作符(!),表示 nav.value 一定不是 null 或 undefined:

`
navWidth = nav.value!.clientWidth;`
这样,当使用 navWidth 变量时,它将被视为已定义。但请注意,确保在使用非空断言操作符之前,nav.value 确实不为 null 或 undefined。

问题2:

是的,当定义一个与 div 关联的 ref 时,你可以使用 HTMLElement 或 HTMLDivElement。通常,如果你不需要访问特定于某种 HTML 元素类型的属性或方法,可以使用更通用的 HTMLElement。然而,如果你需要访问特定于 div 元素的属性或方法(例如,可能在将来的 HTML 规范中添加的内容),则可以使用 HTMLDivElement。

对于没有特定 HTML 接口的元素(如 nav 标签),可以使用 HTMLElement 类型。在实际操作中,这样做通常足够满足需求。使用 HTMLDivElement 或其他特定类型的优点在于,它们提供了针对特定元素的属性和方法,从而使得类型检查更加严格,帮助避免潜在的错误。

总结起来你的问题应该是 泛型参数有什么作用

举个栗子:

假设我们写了一个使用id来获取页面上的一个element的方法

function getElement(id: string): HTMLElement | null {
    return document.getElementById(id);
}

然后,使用它

<input type='text' id='input1' value='value1' />
const element = getElement('input1');

// 但是因为 HTMLElement 不具备 value 属性,这样写报错
// const v = element.value;

// 可我们知道它就是input,所以可以把它类型转换一下,并顺利拿到值
const v = (element as HTMLInputElement).value;

这个 getElement 用起来是不是挺麻烦的?
在我们预知类型的情况下,还要自己做转换,直接帮我转换了不就得了!
改造一下

function getElement<TElement>(id: string): TElement {
    return document.getElementById(id) as TElement;
}

再使用它

// 因为我们知道input1是确实存在且是 HTMLInputElement 类型的,所以直接代入类型
const element = getElement<HTMLInputElement>('input1');
// 拿到的 element 就是 HTMLInputElement 类型的,直接取值
const v = element.value;

现在,再去看 ref 的类型参数该怎么代入是不是就明白了?
ref 代入什么类型对应的就是它 value 的类型,大概如下

function ref<T>(...): T {
    return ({ value: ... as T });
}

问题1
是的,这么写更好。

问题2
是的 HTMLDivElement 会比 HTMLElement 多一些属性。虽然你可能用不上。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Microsoft
子站问答
访问
宣传栏