在 Vue 3.5 版本里,新引入的 useId API 为开发者带来了诸多便利。
有关 useId API 的详细文档,请参阅 Vue Composition API Helpers。
从原理上讲,useId 函数主要依赖 getCurrentInstance 这个 API 来生成唯一 ID。当调用 getCurrentInstance 时,会获取到当前 Vue 实例的信息对象,其中包含一个 ids 数组。useId 利用这个数组中的元素,结合可全局配置的 idPrefix(若未配置则默认为 ‘v’)来生成最终的唯一 ID。例如在代码实现中:
export function useId(): string {
const i = getCurrentInstance()
if (i) {
return (i.appContext.config.idPrefix || 'v') + '+' + i.ids[0] + i.ids[1]++
} else if (_DEV_) {
warn('useId() is called when there is no active component instance to be associated with.')
}
return
}
在实际应用场景中,useId 表现出色。
在表单场景下,我们常常需要实现点击 label 标签时能自动聚焦到对应的表单项。以往可能需要手动管理 ID 以确保两者一致,现在借助 useId 就可以轻松实现。像下面这样:
<script setup>
import { useId } from 'vue'
const id = useId()
</script>
<label :for="id">名称</label>
<input :id="id" />
当使用 v-for 循环渲染列表时,为了优化 DOM 更新性能,必须给每个遍历项提供一个唯一的 key。useId 在此处就派上了用场,如:
const data = new Array(100).fill(0).map((_, index) => ({
label: `文本${index}`,
value: useId(), // 生成唯一 ID
}))
<ul>
<li v-for="item in data" :key="item.key">{{ item.label }}({{ item.key }})</li>
</ul>
在服务端渲染(SSR)方面,由于页面 HTML 先在服务器生成再在客户端激活,如果服务器和客户端生成的 ID 不一致,就会导致问题。例如在一个典型的 SSR 应用中,服务端代码可能如下:
// 服务端代码
import { createSSRApp } from 'vue'
import { renderToString } from '@vue/server-renderer'
import App from './App.vue'
const app = createSSRApp(App)
// 假设在这里获取了一些数据
const data = fetchData()
renderToString(app).then((html) => {
// 将服务端渲染的 HTML 发送给客户端
sendToClient(html)
})
客户端代码:
// 客户端代码
import { createSSRApp } from 'vue'
import App from './App.vue'
const app = createSSRApp(App)
// 客户端激活,将服务端渲染的 HTML 转换成可交互的页面
hydrateApp(app)
在 App.vue 组件内:
<template>
<div>
<input :id="inputId" type="text" />
<label :for="inputId">Enter text:</label>
</div>
</template>
<script setup>
import { useId } from 'vue'
const inputId = useId()
</script>
通过 useId 生成的 ID 在服务端和客户端保持一致,有效避免了 ID 冲突,确保了表单元素等功能的正常运行。若使用 Math.random() 或 Date.now() 等方式生成 ID,很容易出现两端不一致的情况,进而引发表单点击聚焦等功能异常。
在使用组件库(如 Element Plus)进行 SSR 开发时,为防止 hydration 错误,保证服务器和客户端 ID 一致至关重要。我们可以通过向 Vue 应用注入 ID_injection_key 来确保组件库生成的 ID 在 SSR 环境下的唯一性,示例代码如下:
import { createApp } from 'vue'
import { ID_INJECTION_KEY } from 'element-plus'
import App from './App.vue'
const app = createApp(App)
app.provide(ID_INJECTION_KEY, {
prefix: 1024,
current: 0,
})
总之,Vue 3.5 的 useId 特性在多个关键开发场景中都发挥了重要作用,显著提升了开发的便利性和应用的稳定性。