前端如何优雅地生成唯一标识?——一份跨环境 UUID 设备函数的封装与实战

news/2025/10/3 19:42:03/文章来源:https://www.cnblogs.com/wzzkaifa/p/19124912

关键词:UUID、crypto.randomUUID、polyfill、兼容性、Node、浏览器、TypeScript

一、背景:为什么不要直接 Math.random()

前端经常需要“全局唯一” id,常见场景:

场景是否可重复后果
上传文件临时名秒传失效 / 覆盖
表单多条动态行删除错位
日志 / 埋点 traceId链路串线

Math.random() 随机熵只有 48 bit,理论碰撞概率 1/2²⁴≈1/16M,在 toB 长生命周期系统里并不安全。
Web 标准早已给出加密级方案:crypto.randomUUID(),但存在三把“隐形锁”:

  1. 仅 HTTPS / localhost(Secure Context)
  2. 2022 年才普及,IE、Chrome<92 直接抛错
  3. Node 需要 ≥14.17,且需 import { randomUUID } from 'node:crypto'

本文目标:一份代码,同时跑在

  • 现代浏览器(优先原生)
  • 旧浏览器(自动降级)
  • Node / SSR(Jest、Next.js、Electron 主进程)
  • 小程序(微信 / 支付宝,提供 polyfill 入口)

二、设计思路:能力探测 + 分层降级

┌─ ① 原生 crypto.randomUUID (最优)
├─ ② crypto.getRandomValues 手动 v4
├─ ③ Node crypto.randomUUID (SSR)
└─ ④ 最末兜底 Math.random(警告)

策略:

  • 运行期探测,不污染全局
  • Tree-shakable,无三方依赖,包体 <1 KB
  • TypeScript 全类型,支持 ESM / CJS 双格式
  • 可Mock,测试友好

三、源码:uuid.ts(零依赖)

/* eslint-disable no-console */
type UUID = `${string}-${string}-${string}-${string}-${string}`;
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
/** 判断当前是否 Secure Context,可解锁 crypto.randomUUID */
const isSecureContext = () =>
typeof globalThis.crypto !== 'undefined' &&
typeof crypto.randomUUID === 'function';
/** 判断 Node 环境 */
const isNode = () =>
typeof process !== 'undefined' &&
process.versions?.node;
/** 使用 crypto.getRandomValues 手动拼 v4 UUID */
const uuidv4ByRandomValues = (): UUID => {
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
// Per RFC4122, set version (4) and variant (1)
bytes[6] = (bytes[6]! & 0x0f) | 0x40;
bytes[8] = (bytes[8]! & 0x3f) | 0x80;
const hex = [...bytes].map(b => b.toString(16).padStart(2, '0'));
return `${hex.slice(0, 4).join('')}-${hex[4]}${hex[5]}-${hex[6]}${hex[7]}-${hex[8]}${hex[9]}-${hex.slice(10, 16).join('')}` as UUID;
};
/** Node 原生 */
const uuidv4ByNode = (): UUID => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { randomUUID } = require('node:crypto') as { randomUUID: () => UUID };
return randomUUID();
};
/** 最末兜底:控制台告警,生产请避免 */
const uuidv4ByMath = (): UUID => {
console.warn('[uuid] falling back to Math.random() — NOT crypto secure!');
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
}) as UUID;
};
/**
* 跨环境 UUID v4 生成器
* @returns RFC4122 标准 UUID
*/
export function uuid(): UUID {
if (isSecureContext()) return crypto.randomUUID() as UUID;
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
return uuidv4ByRandomValues();
}
if (isNode()) return uuidv4ByNode();
return uuidv4ByMath();
}
/** 校验字符串是否为合法 UUID */
export const isUUID = (str: string): str is UUID => UUID_REGEX.test(str);

四、使用示例

1. ESM(Vite / Webpack)

import { uuid } from '@/utils/uuid';
console.log(uuid()); // a1b2c3d4-e5f6-4g8h-ij90-klmnopqrstuv

2. CommonJS(Node 脚本)

const { uuid } = require('./dist/uuid.cjs');
console.log(uuid());

3. 小程序入口 polyfill(微信)

微信没有 crypto,在 app.ts 顶部一次性注入:

import 'polyfill-crypto-methods'; // npm i polyfill-crypto-methods
// 现在全局有 crypto.randomUUID / getRandomValues

4. Jest 测试固定值

jest.mock('@/utils/uuid', () => ({ uuid: () => 'test-uuid-123' }));

五、性能 & 体积对比

方案包体 (gzip)生成 10w 次 (Chrome 119)备注
本文函数0.3 KB85 ms原生最快
uuid@9 库4.2 KB95 ms稳定,需安装
nanoid@51.1 KB65 ms非标准 UUID 格式

测试机:M1 Mac / Node 20
结论:现代浏览器直接用原生,包体≈0;旧端自动降级,无需业务感知。

六、踩坑记录

坑位现象解决
crypto.randomUUID 在 http 线下 502报错 TypeError: crypto.randomUUID is not a function先探测 isSecureContext,再降级
Node 14 找不到 randomUUID同上报错require('node:crypto') 动态导入
小程序 undefined没有全局 crypto引入 polyfill-crypto-methods
Webpack 4 把 process 打进包导致 isNode() 误判配置 node: { process: false }

七、FAQ

Q1. 为什么不用 Date.now() + Math.random()
→ 时间戳可被预测,且碰撞概率高,不符合 RFC4122。

Q2. 能否生成 v1/v5?
→ 本函数专注无状态 v4。需要 v1(带 MAC+时间)或 v5(基于命名空间哈希)请用 uuid 库。

Q3. SSR 会重复吗?
→ 不会。Node 的 crypto.randomUUID 同样加密级随机。

Q4. 要支持 IE11?
→ 需额外 crypto polyfill(如 crypto-js),但 IE 已正式退役,建议推动业务升级。

八、总结一句话

“先探测、后降级、零依赖、可 Tree-shaking”——把复杂度封装在工具里,业务代码永远只需 uuid()

如果本文对你有帮助,点个赞或在评论区交流更多“唯一序列号”玩法~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/926249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

广州网站建设外包公司网站建设情况自查报告

1、syntax"proto3":表明使用的是proto3格式&#xff0c;如果不指定则为proto22、package test:定义包名为test&#xff0c;生成类时&#xff0c;会产生一个目录为test3、message Person:消息主体内容&#xff0c;里面为各个字段的定义二、生成对应的PHP类定义好Perso…

解码红黑树

红黑树全面解析:从设计逻辑到代码落地 红黑树是自平衡二叉搜索树(Self-Balanced BST) 的经典实现,核心解决了普通 BST 失衡、AVL 树过度平衡的问题。它通过 “颜色约束” 实现 “大致平衡”,兼顾查找性能与插入 /…

苹果首款折叠屏iPhone全爆料汇总:明年9月发布、1.3万元起步

苹果首款折叠屏iPhone全爆料汇总:明年9月发布、1.3万元起步Posted on 2025-10-03 19:30 lzhdim 阅读(0) 评论(0) 收藏 举报从多方权威爆料基本可以确定,苹果将会在明年9月发布首款可折叠iPhone,与iPhone 18 Pr…

英文笔记

column 列 float 浮动 margin 边距 font 字体 border 边框 radius 圆角 background 背景 color 颜色 padding 填充

苹果最强平板!M5版iPad Pro开箱上手提前泄露:升级12GB内存 GPU性能大涨

苹果最强平板!M5版iPad Pro开箱上手提前泄露:升级12GB内存 GPU性能大涨Posted on 2025-10-03 19:30 lzhdim 阅读(0) 评论(0) 收藏 举报日前,俄罗斯博主Wylsacom提前发布M5芯片版iPad Pro开箱视频,甚至还在Gee…

深入解析:深入MySQL、JVM与Maven核心原理​

深入解析:深入MySQL、JVM与Maven核心原理​2025-10-03 19:32 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: bl…

创世网站建设 优帮云山西省住房和城乡建设厅门户网官方网站

上一次我们讲了使用Azure DevOps Pipeline实现.Net Core程序的CI。这次我们来演示下如何使用Azure DevOps实现.Net Core程序的CD。实现本次目标我们除了Azure DevOps外还需要&#xff1a;一台安装了Docker的主机一个 Docker Hub 账号上一次我们的CI实现了&#xff1a;发布>编…

网站域名备案后公示南宁做网站比较好的公司

目录 1.动态内存分配的原因 2.动态内存函数的介绍 2.1malloc和free函数 2.2calloc函数 2.3realloc函数 3. 常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 使用free释放一块动态开辟内存的一部…

10/3

今日下起了大雨,我在家背诵了单词

专注手机网站建设中国工程建设管理协会网站

前言 关于 .NET Core 旧版本的 sdk 介绍可以参看我以前的 这篇 文章。 8 个小时前&#xff0c;.NET Core 项目组释放了 .NET Core 新一轮的 sdk 工具更新&#xff0c;即 RC4 版本 &#xff0c;这个版本也就是意味着基本功能已经确定了&#xff0c;下个版本应该就是RTM版了&…

后台网站模板html口碑营销的优缺点

Java中的输入输出&#xff08;I/O&#xff09;流是用于读取和写入数据的机制。在Java中&#xff0c;I/O流被设计为按照流的方向和数据源/目标类型进行分类。流的方向分为输入流和输出流&#xff0c;而数据源/目标类型则分为字节流和字符流。 流的方向&#xff1a; 输入流&…

推荐一款集成AI能力的数据库管理工具

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Netflix确保数亿用户观影体验的“事件”管理是如何构建与实践的?

在流媒体服务随时可能因系统故障而中断的时代,Netflix 如何确保数亿用户的观影体验始终稳定?本文将揭晓其技术团队的关键实践 —— 从中央 SRE 团队独揽事件管理,到让每个工程师都能主动发起并处理事件的转型之路。…

为什么词嵌入可以和位置编码相加

一、引言 一个非常朴素的直觉是: 词嵌入是语义,位置编码是词的位置。这两者本身是不同的量纲,就跟身高和体重一样,他们之间有关系,而且我们可以找到他们之间的关系,比如BMI,但是直接把身高(cm)和体重(kg)加…

【比赛记录】2025CSP-S模拟赛57

A B C D Sum Rank100 60 15 - 175 8/22A. 开挂 首先我们希望总步数最小,排序后一次使每个数成为大于它的最小的数即可。 然后根据排序不等式,我们希望修改操作尽可能的集中,倒着扫即可。此时需要确定比这个数大的最…

实用指南:软件设计师——04 操作系统

实用指南:软件设计师——04 操作系统pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mona…

优化排名推广技术网站wordpress 模板 怎么用

在物质世界的繁华背后&#xff0c;隐藏着一个深刻的真理&#xff1a;有形之物的分享会逐渐减少&#xff0c;而无形之物的传递却能不断增值。金钱、货币、银两这些商业领域的实体&#xff0c;往往激发出人类对更多财富的渴望和对资源枯竭的恐惧。这种恐惧源于资源的有限性&#…

移动网站开发工具网站权限怎么设置方法

本篇主要讲Animation 和 Property Animation的使用&#xff0c;最后会讲QQ管家桌面火箭作为例子&#xff1a; 在Android中开发动效有两套框架可以使用&#xff0c;分别为 Animation 和 Property Animation&#xff1b; 相对来说&#xff0c;Animator比Animation要强大太多&…

实用指南:洛谷题解——C语言(9.17——9.19)

实用指南:洛谷题解——C语言(9.17——9.19)2025-10-03 19:14 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: …