所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

Vue 3 useId 完全指南:生成唯一标识符的最佳实践

📖 概述

useId() 是 Vue 3 中的一个组合式 API 函数,用于生成唯一的标识符。它确保在服务端渲染(SSR)和客户端渲染之间生成一致的 ID,避免水合不匹配的问题。

🎯 基本概念

什么是 useId?

useId() 返回一个唯一的字符串标识符,该标识符在组件的整个生命周期内保持不变,并且在服务端和客户端之间保持一致。

使用场景

  • 🏷️ 为表单元素生成唯一 ID
  • 🔗 关联 label 和 input 元素
  • 🎯 为 ARIA 属性提供唯一标识
  • 🔍 为测试提供稳定的选择器

🔧 函数签名

function useId(): string;

💻 代码示例

🚀 基础用法

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

// 生成唯一标识符
const id = useId();

console.log(id); // 例如: "v-1a2b3c4d"
</script>

<template>
  <div>
    <label :for="id">用户名</label>
    <input :id="id" type="text" />
  </div>
</template>

🏷️ 表单元素关联

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

const inputId = useId();
const checkboxId = useId();
</script>

<template>
  <div class="form-group">
    <label :for="inputId">邮箱地址</label>
    <input :id="inputId" type="email" placeholder="请输入邮箱" />

    <div class="checkbox-group">
      <input :id="checkboxId" type="checkbox" />
      <label :for="checkboxId">订阅新闻</label>
    </div>
  </div>
</template>

🎯 ARIA 属性支持

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

const dialogId = useId();
const descriptionId = useId();
</script>

<template>
  <div
    role="dialog"
    :aria-labelledby="dialogId"
    :aria-describedby="descriptionId"
  >
    <h2 :id="dialogId">确认删除</h2>
    <p :id="descriptionId">此操作不可撤销,请确认是否继续。</p>

    <button @click="confirm">确认</button>
    <button @click="cancel">取消</button>
  </div>
</template>

🔍 多个组件实例

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

// 每个组件实例都会生成不同的 ID
const containerId = useId();
const listId = useId();
</script>

<template>
  <div :id="containerId" class="dropdown">
    <button :aria-expanded="isOpen" :aria-controls="listId">选择选项</button>

    <ul :id="listId" role="listbox" v-show="isOpen">
      <li role="option" v-for="item in items" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

⚖️ 与其他 ID 生成方式的对比

❌ 不推荐的方式

<script setup lang="ts">
// 使用 Math.random() - 服务端和客户端不一致
const randomId = Math.random().toString(36).substr(2, 9);

// 使用 Date.now() - 可能导致重复
const timestampId = Date.now().toString();
</script>

✅ 推荐使用 useId

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

// 使用 useId - 服务端和客户端一致
const stableId = useId();
</script>

⚠️ 注意事项

🔢 ID 唯一性

useId() 生成的 ID 在同一个组件实例中是唯一的,但在不同的组件实例中会不同:

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

const id1 = useId(); // 第一个组件实例的 ID
const id2 = useId(); // 第二个组件实例的 ID

// id1 !== id2
</script>

📝 SSR 兼容性

useId() 确保在服务端渲染和客户端水合时生成相同的 ID:

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

const id = useId();

// 在服务端和客户端都会生成相同的 ID
// 避免了水合不匹配的问题
</script>

🛡️ 使用限制

useId() 只能在组件的 setup 函数或 <script setup> 中使用:

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

// ✅ 正确:在 setup 中使用
const id = useId();

// ❌ 错误:不能在事件处理函数中使用
const handleClick = () => {
  const newId = useId(); // 这会导致错误
};
</script>

🎯 最佳实践

1️⃣ 为表单元素生成 ID

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

const inputId = useId();
const textareaId = useId();
const selectId = useId();
</script>

<template>
  <form>
    <div class="form-field">
      <label :for="inputId">姓名</label>
      <input :id="inputId" type="text" />
    </div>

    <div class="form-field">
      <label :for="textareaId">描述</label>
      <textarea :id="textareaId"></textarea>
    </div>

    <div class="form-field">
      <label :for="selectId">城市</label>
      <select :id="selectId">
        <option value="beijing">北京</option>
        <option value="shanghai">上海</option>
      </select>
    </div>
  </form>
</template>

2️⃣ 组合多个 useId

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

const baseId = useId();
const inputId = `${baseId}-input`;
const labelId = `${baseId}-label`;
const errorId = `${baseId}-error`;
</script>

<template>
  <div class="form-control">
    <label :id="labelId" :for="inputId">密码</label>
    <input
      :id="inputId"
      :aria-labelledby="labelId"
      :aria-describedby="errorId"
      type="password"
    />
    <span :id="errorId" class="error-message"> 密码长度至少6位 </span>
  </div>
</template>

3️⃣ 在可复用组件中使用

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

interface Props {
  label?: string;
  error?: string;
}

const props = defineProps<Props>();
const inputId = useId();
const errorId = useId();
</script>

<template>
  <div class="input-wrapper">
    <label v-if="label" :for="inputId">{{ label }}</label>
    <input :id="inputId" :aria-describedby="errorId" v-bind="$attrs" />
    <span v-if="error" :id="errorId" class="error">
      {{ error }}
    </span>
  </div>
</template>

❓ 常见问题

Q: useId 生成的 ID 格式是什么?

A: useId() 生成的 ID 格式类似 "v-1a2b3c4d",以 “v-” 开头,后跟随机字符串。

Q: 可以在条件渲染中使用 useId 吗?

A: 可以,但要注意 ID 在组件的整个生命周期内保持不变,即使元素被条件渲染。

Q: useId 和 ref 有什么区别?

A: useId() 用于生成唯一标识符,ref() 用于创建响应式引用。它们的用途完全不同。

📝 总结

useId() 是 Vue 3 中处理唯一标识符的标准方式,特别适用于表单元素、ARIA 属性和 SSR 场景。它确保了服务端和客户端之间的一致性,避免了水合不匹配的问题。在需要唯一 ID 的场景中,始终优先使用 useId() 而不是其他随机生成方式。

原文链接:https://code.ifrontend.net/archives/1097,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?