Vue3.5新增的useId到底有啥用?

0. 啥是useId

Vue 3.5中新增的useId函数主要用于生成唯一的ID,这个ID在同一个Vue应用中是唯一的,并且每次调用useId都会生成不同的ID。这个功能在处理列表渲染、表单元素和无障碍属性时非常有用,因为它可以确保每个元素都有一个唯一的标识符。

useId的实现原理相对简单。它通过访问Vue实例的ids属性来生成ID,这个属性是一个数组,其中包含了用于生成ID的前缀和自增数字。每次调用useId时,都会取出当前的数字值,然后进行自增操作。这意味着在同一页面上的多个Vue应用实例可以通过配置app.config.idPrefix来避免ID冲突,因为每个应用实例都会维护自己的ID生成序列。

1. 实现源码

ts 复制代码
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 ''
}
  • i.appContext.config.idPrefix:这是从当前组件实例中获取的一个配置属性,用于定义生成ID的前缀。如果这个前缀存在,它将被使用;如果不存在,默认使用 'v'
  • i.ids[0]:这是当前组件实例上的 ids 数组的第一个元素,它是一个字符串,通常为空字符串,用于生成ID的一部分。
  • i.ids[1]++:这是 ids 数组的第二个元素,它是一个数字,用于生成ID的自增部分。这里使用了后置自增运算符 ++,这意味着它会返回当前值然后自增。每次调用 useId 时,这个数字都会增加,确保生成的ID是唯一的。

2.设置ID前缀

如果不想使用默认的前缀'v'的话,可以通过app.config.idPrefix进行设置。

ts 复制代码
const app = createApp(App)

app.config.idPrefix = 'vid'

3.使用场景

3-1. 表单元素的唯一标识

在表单中,<label> 标签需要通过 for 属性与对应的 <input> 标签的 id 属性相匹配,以实现点击标签时输入框获得焦点的功能。使用 useId 可以为每个 <input> 元素生成一个唯一的 id,确保这一功能的正常工作。例如:

html 复制代码
<label :for="id">Do you like Vue 3.5?</label>
  <input type="checkbox" :id="id" />
ts 复制代码
const id = useId()

3-2. 列表渲染中的唯一键

在渲染列表时,每一项通常需要一个唯一的键(key),以帮助 Vue 追踪每个节点的身份,从而进行高效的 DOM 更新。如果你的列表数据没有唯一key的话,那么useId 可以为列表中的每个项目生成一个唯一的键。

html 复制代码
<ul>
    <li v-for="item in items" :key="item.id">
      {{ item.text }}({{ item.id }})
    </li>
  </ul>
ts 复制代码
const items = Array.from({ length: 10}, (v, k) => { 
    return {
        text: `Text ${k}`,
        id: useId()
    }
})

上述代码渲染结果如下:

3-3. 服务端渲染(SSR)中避免 ID 冲突

在服务端渲染(SSR)的应用中,页面的HTML内容是在服务器上生成的,然后发送给客户端浏览器。在客户端,浏览器会接收到这些HTML内容,并将其转换成一个可交互的页面。如果在服务器端和客户端生成的HTML中存在相同的ID,那么在客户端激活(hydrate)时,就可能出现问题,因为客户端可能会尝试操作一个已经由服务器端渲染的DOM元素,导致潜在的冲突或错误。

下面是一个使用useId来避免这种ID冲突的实际案例:

服务端代码 (server.js)

js 复制代码
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);
});

客户端代码 (client.js)

js 复制代码
import { createSSRApp } from 'vue';
import App from './App.vue';

const app = createSSRApp(App);

// 客户端激活,将服务端渲染的HTML转换成可交互的页面
hydrateApp(app);

在这个案例中,无论是服务端还是客户端,我们都使用了createSSRApp(App)来创建应用实例。如果我们在App.vue中使用了useId来生成ID,那么这些ID将在服务端渲染时生成一次,并在客户端激活时再次使用相同的ID。

App.vue 组件

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>

App.vue组件中,我们使用了useId来为<input>元素生成一个唯一的ID。这个ID在服务端渲染时生成,并包含在发送给客户端的HTML中。当客户端接收到这个HTML并开始激活过程时,由于useId生成的ID在服务端和客户端是相同的,所以客户端可以正确地将<label>元素关联到<input>元素,而不会出现ID冲突的问题。

如果没有使用useId,而是使用了Math.random()Date.now()来生成ID,那么服务端和客户端可能会生成不同的ID,导致客户端在激活时无法正确地将<label><input>关联起来,因为它们具有不同的ID。这可能会导致表单元素的行为异常,例如点击<label>时,<input>无法获得焦点。

3-4. 组件库中的 ID 生成

在使用 Element Plus 等组件库进行 SSR 开发时,为了避免 hydration 错误,需要确保服务器端和客户端生成相同的 ID。通过在 Vue 中注入 ID_injection_key,可以确保 Element Plus 生成的 ID 在 SSR 中是唯一的。

js 复制代码
// src/main.js
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,
})

希望这篇文章介绍对你有所帮助,上述代码已托管在Gitee上,欢迎自取!

相关推荐
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
吹牛不交税7 小时前
admin.net-v2 框架使用笔记-netcore8.0/10.0版
vue.js·.netcore