Nuxt3中创建SSR安全的唯一ID生成组合式函数遇重复ID问题
问题:创建SSR安全的全局递增ID组合式函数
问题描述
需要实现一个组合式函数,每次调用时递增全局计数器并返回唯一值,同时保证SSR安全(服务端渲染的ID与客户端hydrate时完全一致)。
现有代码:
// composables/useId.ts let id = 0 export default function () { if (id >= Number.MAX_SAFE_INTEGER) id = Number.MIN_SAFE_INTEGER const incrementedId = id++ return useState(() => incrementedId) }
使用示例:
<ul> <li v-for="_ in 10"> ID is: {{ useId() }} </li> </ul>
问题现象:所有元素的ID值完全相同,原因是useState默认基于调用位置生成key,多次调用复用同一状态,导致返回值一致。
核心需求:保证HTML ID唯一性,例如在循环渲染组件时,每个组件实例的label与input能正确绑定:
// 父组件 <template> <MyInput v-for="_ in 10" /> </template> // MyInput.vue <template> <label :for="generatedId">Something</label> <input type="text" :id="generatedId"> </template> <script lang="ts" setup> const generatedId = useId() </script>
解决方案
问题根源分析
- 原代码中
useState无自定义key,多次调用会复用同一状态,导致ID重复。 - 全局变量
id在服务端会被多个请求共享,引发跨请求ID冲突。
修正后的实现
借助Nuxt的useState实现请求级隔离的计数器,同时保证SSR状态同步:
// composables/useId.ts export default function useId(prefix = 'id') { // 用全局共享的useState维护计数器,服务端每个请求独立初始化,客户端继承服务端状态 const counter = useState('__unique_id_counter__', () => 0) // 先获取当前值再递增,确保ID序列连续唯一 const currentId = counter.value counter.value += 1 // 处理计数器溢出,重置到安全整数范围 if (counter.value >= Number.MAX_SAFE_INTEGER) { counter.value = Number.MIN_SAFE_INTEGER } // 返回符合HTML规范的字符串ID(前缀+数字) return `${prefix}-${currentId}` }
使用说明
- 在组件中直接调用即可获得唯一ID:
<script lang="ts" setup> const generatedId = useId() // 或自定义前缀 const inputId = useId('user-input') </script> - 循环场景下,每个调用会返回递增的唯一ID,完全满足需求。
为什么可行
useState('__unique_id_counter__')在服务端为每个请求创建独立计数器,避免跨请求ID冲突;客户端会复用服务端序列化后的状态,保证SSR前后ID一致。- 每次调用递增计数器,确保ID全局唯一。
- 返回带前缀的字符串ID,符合HTML ID命名规范(不能以数字开头)。
内容的提问来源于stack exchange,提问作者Matej
相关产品推荐
相关产品推荐

