JavaScript UUID v4 生成完全指南
直接在浏览器中使用免费的 UUID v4生成器,无需安装。
在线试用 UUID v4生成器 →每个 JavaScript 应用程序最终都需要唯一标识符——会话令牌、数据库行、 支付 API 的幂等键、分布式追踪的关联 ID。今天在 JavaScript 中 生成 UUID v4 最简单的方式是 crypto.randomUUID(): 零依赖,一行代码,密码学安全。UUID v4 被广泛使用, 正是因为它不需要服务间协调——客户端和服务端都可以独立生成 ID
而无需担心碰撞。本指南涵盖该内置 API、 uuid npm 包、 验证方法——全部适用于 Node.js 19+ 和现代浏览器。如果需要免代码选项, ToolDeck 上的 UUID v4 生成器 可即时生成符合规范的标识符。
- ✓crypto.randomUUID() 内置于浏览器和 Node.js——零依赖,一行代码。
- ✓UUID v4 是 128 位随机标识符:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx(y 为 8、9、a 或 b)。
- ✓使用 /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i 验证——检查版本位和变体位。
- ✓当需要随机 ID 以外的功能时,uuid npm 包可添加 v1、v3、v5 和 v7 支持。
- ✓对于数据库主键,优先选择 UUID v7(时间有序)而非 v4(随机),以减少索引碎片化。
什么是 UUID v4?
UUID(通用唯一标识符)第 4 版是一个 128 位随机标识符,格式为 32 个十六进制数字,由四个连字符分隔: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx。 位置 15 处的 4 标记版本。 位置 20 处的 y 字符为 8、 9、 a 或 b(RFC 4122 变体)之一。 其余 122 位为随机数。UUID v4 是 JavaScript 应用程序中最常用的版本, 因为它不需要系统间协调——可以在客户端和服务端独立生成 ID,无需担心碰撞。
// No identifier
const event = { action: "user.login", ts: 1711824000 };// With UUID v4
const event = {
id: "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
action: "user.login",
ts: 1711824000
};crypto.randomUUID() — JavaScript 原生方式
crypto.randomUUID() 可在 Chrome 92+、Firefox 95+、Safari 15.4+ 和 Node.js 19+ 中使用(从 Node.js 14.17.0 起通过 globalThis.crypto 提供实验性支持)。 它返回一个小写 36 字符的 UUID v4 字符串。在浏览器 JavaScript 中无需导入。 在 Node.js 中,可以直接在全局 crypto 对象上调用, 或从 node:crypto 模块显式导入。
// 适用于任何现代浏览器——无需构建步骤,无需打包工具
const requestId = crypto.randomUUID();
console.log(requestId);
// "3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f"
// 在需要唯一标识符的任何地方使用
const telemetryEvent = {
event_id: crypto.randomUUID(),
action: "checkout.started",
session_id: crypto.randomUUID(),
timestamp: Date.now(),
cart_total_cents: 14999,
};
console.log(JSON.stringify(telemetryEvent, null, 2));// 方式 1:全局 crypto(Node.js 19+)
const orderId = crypto.randomUUID();
console.log(orderId);
// "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
// 方式 2:从 node:crypto 显式导入
import { randomUUID } from 'node:crypto';
const correlationId = randomUUID();
console.log(correlationId);
// "f7e6d5c4-b3a2-4190-8f7e-6d5c4b3a2190"如果代码可能运行在旧版浏览器或嵌入式 WebView 中,对 crypto.randomUUID 进行特性检测是值得的。检查只需一个 typeof 守卫:
function generateUUIDv4() {
// Prefer the native API when available
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
return crypto.randomUUID();
}
// Manual fallback using getRandomValues (see next section)
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
// Set version (4) and variant (RFC 4122)
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx
const hex = [...bytes].map(b => b.toString(16).padStart(2, '0'));
return [
hex.slice(0, 4).join(''),
hex.slice(4, 6).join(''),
hex.slice(6, 8).join(''),
hex.slice(8, 10).join(''),
hex.slice(10, 16).join(''),
].join('-');
}
console.log(generateUUIDv4());
// "9b2e4f1a-7c3d-4e8f-a5b6-0d2c1e9f8a7b"crypto.randomUUID() 始终可用。在某些浏览器中, 它会在非安全 HTTP 页面上抛出错误。如果您的应用在开发期间有时在纯 HTTP 上运行, 上面的 getRandomValues 回退方案可以处理这种情况。不使用库生成 UUID v4
有时无法依赖 crypto.randomUUID()—— 也许您的目标 WebView 剥离了 crypto API, 或者您需要了解底层原理。手动方式使用 crypto.getRandomValues() (自 IE 11 起可用)填充 16 字节随机数据,然后应用两个位掩码操作 来设置版本和变体字段。这两个操作是 UUID v4 和纯随机字节字符串之间的唯一区别。
function uuidv4Manual() {
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
// Set version: bits 12–15 of byte 6 = 0100 (version 4)
bytes[6] = (bytes[6] & 0x0f) | 0x40;
// Set variant: bits 6–7 of byte 8 = 10 (RFC 4122)
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const hex = [...bytes].map(b => b.toString(16).padStart(2, '0'));
return (
hex.slice(0, 4).join('') + '-' +
hex.slice(4, 6).join('') + '-' +
hex.slice(6, 8).join('') + '-' +
hex.slice(8, 10).join('') + '-' +
hex.slice(10, 16).join('')
);
}
const traceId = uuidv4Manual();
console.log(traceId);
// "e4d7c2a1-3f9b-48e5-a612-9d8c7b6a5f4e"
// Verify it passes UUID v4 validation
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
console.log(UUID_V4_RE.test(traceId)); // trueMath.random() 来生成 UUID。它不具备 密码学安全性,输出周期太短,某些引擎会产生可预测的序列。请始终使用 crypto.getRandomValues() 或 crypto.randomUUID()。uuid npm 包——多版本支持
npm 上的 uuid 包在 crypto.randomUUID() 出现之前一直是 JavaScript 的首选 UUID 库。在以下三种情况下仍然值得使用:需要 v4 以外的 UUID 版本 (v1、v3、v5、v7),目标运行时低于 Node.js 14.17.0,或需要 validate 和 parse 工具函数。对于在现代运行时上使用纯 UUID v4,原生 API 已经足够, 建议跳过这个依赖。
npm install uuid
import { v4 as uuidv4, validate, version } from 'uuid';
// Generate a UUID v4
const paymentId = uuidv4();
console.log(paymentId);
// "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed"
// Validate any UUID string
console.log(validate(paymentId)); // true
console.log(validate("not-a-uuid")); // false
// Detect the version of a UUID
console.log(version(paymentId)); // 4
// Generate a batch for seeding a test database
const testAccounts = Array.from({ length: 5 }, () => ({
account_id: uuidv4(),
plan: "starter",
created_at: new Date().toISOString(),
}));
console.log(testAccounts);uuid 包在浏览器中内部使用 crypto.getRandomValues(),在 Node.js 中使用 crypto.randomBytes(),因此熵来源与原生 API 完全相同。区别仅在于额外的工具函数和多版本支持。如果不想编写任何代码,可以试用 UUID v4 生成器 ——它可以一键直接在浏览器中生成符合 RFC 4122 的 v4 标识符。
确定性 UUID——从字符串生成 UUID v5
UUID v4 从定义上是随机的——调用两次总会得到不同结果。有时您需要相反的效果: 相同的输入字符串应始终产生相同的 UUID。这正是 UUID v5 的用途。 它使用 SHA-1 对命名空间 UUID 和输入字符串进行哈希,然后将结果格式化为 UUID。 相同命名空间 + 相同输入 = 相同输出,在任何机器上的任何时间都如此。 这对于从 URL、电子邮件地址或任何已标识资源的字符串派生稳定 ID 非常有用。
import { v5 as uuidv5 } from 'uuid';
// Built-in namespace for URLs (RFC 4122)
const URL_NAMESPACE = uuidv5.URL;
// "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
// Same URL always produces the same UUID
const pageId1 = uuidv5("https://api.warehouse.dev/products/sku-7291", URL_NAMESPACE);
const pageId2 = uuidv5("https://api.warehouse.dev/products/sku-7291", URL_NAMESPACE);
console.log(pageId1 === pageId2); // true
console.log(pageId1);
// "a6e4e1c0-7e23-5d3b-8f14-9c2a1b3d5e7f"
// Custom namespace for your application
const APP_NAMESPACE = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
const tenantId = uuidv5("acme-corp", APP_NAMESPACE);
console.log(tenantId);
// "d4735e3a-265b-564e-8f32-7a1b2c3d4e5f"补充说明:UUID v3 做的是同样的事情,但使用 MD5 而不是 SHA-1。新项目建议优先选择 v5。 MD5 存在已知的碰撞弱点,虽然这对 ID 生成影响不大, 但在两者都可用的情况下没有理由选择 MD5 而不是 SHA-1。
crypto.randomUUID() 及相关 API 参考
原生 crypto.randomUUID() 不接受任何参数——它只返回一个字符串。当需要零依赖的 RFC 4122 合规标识符时使用它。 当需要原始随机字节而不是格式化的 UUID 字符串时——例如填充类型化数组或派生密钥—— 直接使用 crypto.getRandomValues()。 与 UUID 工作相关的 API 列举如下。
UUID v4 正则表达式模式分解:
使用正则表达式验证 UUID v4
验证字符串是否为正确的 UUID v4 非常常见——传入的 API 请求体、URL 参数、 Webhook 载荷都需要验证。当需要零依赖且只验证 v4 时,手写正则表达式是正确选择。 如果已经使用 uuid 包,建议改用其 validate() 导出——它处理所有 UUID 版本,比维护自定义模式更不容易出错。正则表达式 /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i 同时检查版本位(必须为 4)和 变体位(必须为 8、 9、 a 或 b)。 布尔检查使用 RegExp.prototype.test(), 从周围文本中提取 UUID 时使用 .match()。
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function isUUIDv4(str) {
return UUID_V4_RE.test(str);
}
// Valid UUID v4
console.log(isUUIDv4("9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d")); // true
// UUID v1 — version nibble is 1, not 4
console.log(isUUIDv4("550e8400-e29b-11d4-a716-446655440000")); // false
// UUID v7 — version nibble is 7
console.log(isUUIDv4("018e4a0c-5b3f-7d12-8a9b-0c1d2e3f4a5b")); // false
// Malformed strings
console.log(isUUIDv4("not-a-uuid")); // false
console.log(isUUIDv4("")); // false
console.log(isUUIDv4("9b1deb4d3b7d4bad9bdd2b0d7b3dcb6d")); // false (no hyphens)
// Extract UUID from a larger string
const logLine = 'Request req_id=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d failed with 503';
const match = logLine.match(/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i);
console.log(match?.[0]);
// "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"从文件和 API 响应中生成并附加 UUID
实际上,很少单独调用 crypto.randomUUID()。 两种模式经常出现:在将记录写入数据库之前为其分配 ID, 以及将关联 ID 附加到出站 API 请求,以便在日志中跨服务追踪请求。
读取 NDJSON 文件 → 分配 UUID → 写回
import { readFileSync, writeFileSync } from 'node:fs';
import { randomUUID } from 'node:crypto';
function assignIds(inputPath, outputPath) {
const lines = readFileSync(inputPath, 'utf-8')
.split('\n')
.filter(line => line.trim());
const records = lines.map(line => {
try {
const record = JSON.parse(line);
if (!record.id) {
record.id = randomUUID();
}
return JSON.stringify(record);
} catch (err) {
console.error(`Skipping malformed line: ${err.message}`);
return null;
}
}).filter(Boolean);
writeFileSync(outputPath, records.join('\n') + '\n');
console.log(`Assigned IDs to ${records.length} records → ${outputPath}`);
}
assignIds('warehouse-products.ndjson', 'warehouse-products-with-ids.ndjson');
// Assigned IDs to 1284 records → warehouse-products-with-ids.ndjson为出站 API 请求附加关联 ID
import { randomUUID } from 'node:crypto';
async function createShipment(orderPayload) {
const correlationId = randomUUID();
try {
const response = await fetch('https://api.logistics.dev/v2/shipments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Correlation-ID': correlationId,
'X-Idempotency-Key': randomUUID(),
},
body: JSON.stringify(orderPayload),
});
if (!response.ok) {
const errorBody = await response.text();
throw new Error(`Shipment API returned ${response.status}: ${errorBody}`);
}
const result = await response.json();
console.log(`Shipment created: ${result.shipment_id} (correlation: ${correlationId})`);
return result;
} catch (err) {
console.error(`[correlation:${correlationId}] Shipment failed: ${err.message}`);
throw err;
}
}
await createShipment({
order_id: "ord_8a3f91bc",
destination: { city: "上海", state: "SH", zip: "200001" },
items: [{ sku: "WH-7291", quantity: 2, weight_kg: 1.4 }],
});命令行 UUID 生成
并非总是需要脚本。Node.js 可以直接从命令行生成 UUID, 这对 Shell 脚本、CI 流水线和快速临时测试非常方便。 -e 标志 用于执行单个表达式。
# Single UUID node -e "console.log(crypto.randomUUID())" # 3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f # Five UUIDs at once node -e "for(let i=0;i<5;i++) console.log(crypto.randomUUID())" # Generate and assign to a shell variable export REQUEST_ID=$(node -e "process.stdout.write(crypto.randomUUID())") echo "Request ID: $REQUEST_ID" # Use npx uuid (if you have the package installed globally or want a one-off) npx uuid v4 # 1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed
# Open any browser DevTools console and type: crypto.randomUUID() # "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
uuidgen 已预装在 macOS 和大多数 Linux 发行版上。 在现代系统上默认生成 UUID v4。高性能替代方案——nanoid
如果每秒需要生成数千个 ID,且 36 字符的 UUID 格式不是硬性要求, 可以考虑 nanoid。 它比 uuid.v4() 快 2 倍, 默认生成 21 字符的 URL 安全 ID,压缩后约 1 KB。 输出不是 UUID——而是自定义字母表 ID——因此不要在需要 RFC 4122 合规性的地方使用它 (数据库 UUID 列、验证 UUID 格式的 API、OpenTelemetry 追踪 ID)。 但对于内部关联 ID、React 组件键和 URL slug,它是很好的选择。
npm install nanoid
import { nanoid, customAlphabet } from 'nanoid';
// Default: 21-char URL-safe ID (A-Za-z0-9_-)
const trackingCode = nanoid();
console.log(trackingCode);
// "V1StGXR8_Z5jdHi6B-myT"
// Custom length
const shortCode = nanoid(10);
console.log(shortCode);
// "IRFa-VaY2b"
// Custom alphabet — numbers only, 12 digits
const numericId = customAlphabet('0123456789', 12);
console.log(numericId());
// "839274651023"
// Custom alphabet — hex only, 32 chars (same entropy as UUID v4 without hyphens)
const hexId = customAlphabet('0123456789abcdef', 32);
console.log(hexId());
// "4f8a1b2c3d7e9f0a5b6c8d1e2f3a4b5c"带语法高亮的终端输出
调试含有大量 UUID 的应用程序意味着在终端盯着大量十六进制字符串。 颜色编码很有帮助。 chalk 库(或 Node.js 21.7+ 中更新的内置 node:util styleText)可以在日志输出中高亮 UUID,使其从周围文本中突出显示。
import chalk from 'chalk';
import { randomUUID } from 'node:crypto';
function logEvent(action, metadata = {}) {
const eventId = randomUUID();
const timestamp = new Date().toISOString();
console.log(
chalk.gray(timestamp),
chalk.cyan(eventId),
chalk.white(action),
Object.keys(metadata).length
? chalk.dim(JSON.stringify(metadata))
: ''
);
}
logEvent('shipment.created', { order_id: 'ord_8a3f', carrier: 'fedex' });
logEvent('payment.captured', { amount_cents: 14999, currency: 'USD' });
logEvent('webhook.delivered', { endpoint: 'https://hooks.acme.dev/orders' });
// 2026-03-27T10:15:00.000Z a1b2c3d4-... shipment.created {"order_id":"ord_8a3f",...}在 JavaScript 中生成短唯一 ID
36 字符的 UUID 有时过长——URL slug、二维码数据、短信和嵌入式硬件协议都有长度限制。
import { randomUUID } from 'node:crypto';
// 1. Strip hyphens from UUID v4 → 32-char hex string
const hex32 = randomUUID().replaceAll('-', '');
console.log(hex32);
// "3e7f1a924b0c4d8e9f127a6b3c8d5e1f" (32 chars)
// 2. Base64-encode 16 random bytes → 22-char string (URL-safe)
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
const base64Id = Buffer.from(bytes)
.toString('base64url')
.replace(/=+$/, '');
console.log(base64Id);
// "Pj8akksNTY6fEnarPIvR" (22 chars, 128 bits of entropy)
// 3. nanoid at custom length
import { nanoid } from 'nanoid';
const short12 = nanoid(12);
console.log(short12);
// "V1StGXR8_Z5j" (12 chars, ~71 bits of entropy)碰撞概率计算:12 字符的 nanoid(默认 64 字符字母表) 提供约 71 位熵。以每秒 1,000 个 ID 的速率,需要大约 116 年才能达到 1% 的碰撞概率。 对大多数应用程序而言绰绰有余。如果每天生成数百万个 ID,请坚持使用完整 UUID 或至少 21 字符的 nanoid。
UUID v7——v4 的时间有序替代方案
UUID v7(RFC 9562 定义)在第一段嵌入 48 位 Unix 毫秒时间戳, 后跟随机位。结果是一个看起来与 v4 相似但按时间顺序排列的 UUID。 这使其成为比 v4 更好的数据库主键选择: 新行总是落在 B 树索引的末尾,而不是随机位置, 从而减少页分裂和碎片化。在需要 Postgres 表的时间有序 ID 的项目中, 我会立即切换到 v7——在大规模下索引性能差异是可以衡量的。ToolDeck 上的 UUID v7 生成器 可以显示任何 v7 UUID 的嵌入时间戳。
import { v7 as uuidv7 } from 'uuid';
// Generate three UUID v7 values — notice they sort chronologically
const id1 = uuidv7();
const id2 = uuidv7();
const id3 = uuidv7();
console.log(id1);
// "018e4a0c-5b3f-7d12-8a9b-0c1d2e3f4a5b"
console.log(id2);
// "018e4a0c-5b40-7e34-9c2d-1e4f5a6b7c8d"
console.log(id3);
// "018e4a0c-5b41-7f56-ae3f-2a5b6c7d8e9f"
// They sort lexicographically by creation time
console.log([id3, id1, id2].sort());
// [id1, id2, id3] — chronological order preserved
// Use v4 for tokens where timing should not be leaked
import { v4 as uuidv4 } from 'uuid';
const sessionToken = uuidv4(); // fully random, no timing infouuid 包从 9.0.0 版本起支持 v7。 如果使用旧版本,运行 npm install uuid@latest 进行升级。在浏览器中无需构建步骤使用 UUID v4
无需打包工具,无需 npm,无需转译器。只需一个普通的 HTML 文件。这是在客户端 JavaScript 中生成 UUID 的最简单方式。 之所以可行,是因为 crypto.randomUUID() 是内置浏览器 API。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>UUID Generator</title></head>
<body>
<p>Your UUID: <strong id="output"></strong></p>
<button onclick="document.getElementById('output').textContent = crypto.randomUUID()">
Generate
</button>
<script>
// Generate one on page load
document.getElementById('output').textContent = crypto.randomUUID();
</script>
</body>
</html>这就是完整的文件。没有 CDN 导入,没有引入库的 script 标签。对于更复杂的需求—— 批量生成、验证、确定性 ID——您需要 uuid 包或前面展示的手动回退方案。但对于快速原型或内部工具,这已经足够。
常见错误
我见过Math.random() UUID 模式从旧博客文章被复制到生产代码中的次数比我愿意承认的要多。 这些模式引入的 bug 是无声的:没有运行时错误,只是在负载下或安全审查中才会浮现的微妙错误行为。
问题: Math.random() 不具备密码学安全性。在某些引擎中其输出是可预测的,低熵使碰撞概率远高于正规的 CSPRNG。
解决方案: 始终使用 crypto.randomUUID() 或 crypto.getRandomValues()。两者都使用操作系统的 CSPRNG。
// INSECURE — predictable, low entropy
function badUuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
/[xy]/g,
c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
}
);
}// SECURE — uses the OS CSPRNG
const id = crypto.randomUUID();
// Or if you need a manual fallback:
function secureUuid() {
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const h = [...bytes].map(b => b.toString(16).padStart(2, '0'));
return `${h.slice(0,4).join('')}-${h.slice(4,6).join('')}-${h.slice(6,8).join('')}-${h.slice(8,10).join('')}-${h.slice(10).join('')}`;
}问题: crypto.randomUUID() 返回小写十六进制,但来自其他系统(数据库、API、用户输入)的 UUID 可能使用大写。当大小写不同时,直接的 === 比较会失败。
解决方案: 在比较之前将两边都规范化为小写。
const fromApi = "9B1DEB4D-3B7D-4BAD-9BDD-2B0D7B3DCB6D"; // uppercase from API
const local = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"; // lowercase from crypto
if (fromApi === local) { /* never runs */ }const fromApi = "9B1DEB4D-3B7D-4BAD-9BDD-2B0D7B3DCB6D";
const local = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d";
if (fromApi.toLowerCase() === local.toLowerCase()) {
// matches correctly
}问题: 某些 UUID 库(尤其是其他语言中的)返回 UUID 对象。在 JavaScript 中,意外地将 UUID 字符串包装在对象中会破坏相等性检查、JSON 序列化和数据库查询。
解决方案: 始终将 UUID 存储和传递为普通字符串。如果库返回对象,立即调用 .toString() 或访问字符串属性。
// Creating unnecessary wrapper
class UUID {
constructor(value) { this.value = value; }
}
const id = new UUID(crypto.randomUUID());
console.log(id === id); // true, but...
console.log(JSON.stringify({ id })); // {"id":{"value":"..."}}// Just use a string
const id = crypto.randomUUID();
console.log(JSON.stringify({ id }));
// {"id":"3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f"}问题: UUID v4 是完全随机的。按 UUID v4 排序会得到任意顺序,而不是创建顺序。这导致不可预测的分页、令人困惑的管理界面和糟糕的数据库索引性能。
解决方案: 需要时间有序标识符时使用 UUID v7。将 UUID v4 保留用于排序顺序无关的令牌和关联 ID。
// Bad: using v4 as a sortable primary key
const rows = [
{ id: crypto.randomUUID(), created: "2026-03-27" },
{ id: crypto.randomUUID(), created: "2026-03-26" },
];
rows.sort((a, b) => a.id.localeCompare(b.id));
// Sort order is random — NOT by creation dateimport { v7 as uuidv7 } from 'uuid';
// Good: v7 sorts by creation time
const rows = [
{ id: uuidv7(), created: "2026-03-27" },
{ id: uuidv7(), created: "2026-03-26" },
];
rows.sort((a, b) => a.id.localeCompare(b.id));
// Sort order matches creation timecrypto.randomUUID() vs uuid vs nanoid——快速对比
对大多数 JavaScript 项目而言:当只需要 UUID v4 且运行时足够新时,使用 crypto.randomUUID()。 当需要 v5(确定性)或 v7(时间有序)支持时,使用 uuid 包。当短的 URL 安全 ID 比 36 字符 UUID 更实用时,使用 nanoid—— 但请记住 nanoid 输出不符合 UUID 规范,在任何期望 RFC 4122 格式的系统上都会验证失败。
如需免代码替代方案,试试 UUID v4 生成器 可在浏览器中即时创建标识符。如需检查现有 UUID,将其粘贴到 UUID 解码器 中可查看其版本、变体和任何嵌入的时间戳数据。
常见问题
如何在 JavaScript 中生成 UUID v4?
在任何现代浏览器(Chrome 92+、Firefox 95+、Safari 15.4+)或 Node.js 19+ 中调用 crypto.randomUUID()。它返回一个小写字符串,如 "3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f"。在浏览器中无需导入,因为 crypto 是内置全局对象;在 Node.js 中也可以使用 import { randomUUID } from "node:crypto" 显式指定模块。对于 Node.js 19 之前的运行时,从 Node.js 14.17.0 起可以在全局对象上调用 crypto.randomUUID()(实验性支持)。如果需要免代码选项,ToolDeck 的 UUID v4 生成器(/en/uuid/v4)可一键生成符合规范的标识符。
const sessionId = crypto.randomUUID(); console.log(sessionId); // "3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f"
crypto.randomUUID() 是密码学安全的吗?
是的。crypto.randomUUID() 使用与 crypto.getRandomValues() 相同的 CSPRNG(密码学安全伪随机数生成器)。UUID v4 中的 122 个随机比特使碰撞概率对所有实际用途来说可以忽略不计——生成大约 271 亿亿个 UUID 才有 50% 的概率发生一次碰撞。熵来源于操作系统的随机源(Linux 上的 /dev/urandom,Windows 上的 BCryptGenRandom),而不是 Math.random(),后者明确不具备密码学安全性。这意味着 UUID v4 值可以安全用作会话令牌、CSRF 令牌以及其他需要避免可预测性的安全敏感标识符。请勿在安全场景中使用基于 Math.random() 的 UUID 生成器。
可以将 UUID v4 用作数据库主键吗?
可以,但存在明显的性能权衡。UUID v4 完全随机,因此 B 树索引碎片化严重——每条新记录都插入到索引的随机位置,而不是末尾。在写入频繁的表上,这会导致过多的页分裂和缓存未命中,从而降低 INSERT 和范围扫描性能。如果数据库支持(PostgreSQL、MySQL 8.0+、SQL Server),UUID v7(时间有序)是更好的主键选择,因为新行总是追加到索引末尾。UUID v4 仍是会话令牌、OAuth 状态参数、幂等键、关联 ID 以及需要隐藏创建时间的字段的正确选择。
// UUID v4 — random, not sortable
const correlationId = crypto.randomUUID();
// UUID v7 — time-ordered, better for DB primary keys
import { v7 as uuidv7 } from 'uuid';
const rowId = uuidv7();UUID v4 和 UUID v7 有什么区别?
UUID v4 用随机数据填充 122 个比特——每个段都是有效的噪声。UUID v7(RFC 9562,2024 年发布)在第一段编码 48 位 Unix 毫秒时间戳,在第二段编码 12 位亚毫秒精度,其余为随机比特。两者总共 128 位,使用相同的 36 字符带连字符格式,因此在存储层可以互换。UUID v7 按创建时间进行字典序排序,使 B 树索引紧凑,并使时间窗口范围查询更高效。当 ID 不应透露创建时间时选择 UUID v4——例如,可能被利用时序信息的面向公众的令牌;对于数据库主键、审计日志和事件流,选择 UUID v7。
如何在 JavaScript 中验证 UUID v4 字符串?
使用正则表达式 /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i 进行测试。位置 15(从 0 开始索引)的字面量 "4" 确认版本位;位置 20 的字符——8、9、a 或 b 之一——编码 RFC 4122 变体位。该正则表达式能正确拒绝 UUID v1、v7 和任何格式错误的字符串。i 标志使其不区分大小写,因此来自其他系统的大写十六进制数字无需规范化即可通过验证。如果只需要知道字符串是否为任何有效的 UUID(不论版本),使用更宽松的模式 /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i 即可。
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
UUID_V4_RE.test("550e8400-e29b-41d4-a716-446655440000"); // true
UUID_V4_RE.test("550e8400-e29b-11d4-a716-446655440000"); // false (v1)
UUID_V4_RE.test("not-a-uuid"); // false如何在 JavaScript 中生成短唯一 ID?
去掉 UUID v4 中的连字符可得到 32 字符的十六进制字符串:crypto.randomUUID().replaceAll("-", "")。如需更短的 ID,使用 nanoid,它默认生成 21 字符的 URL 安全 ID(A–Z、a–z、0–9、_ 和 -),碰撞抗性与完整 UUID 相当。权衡很直接:更短的 ID 碰撞概率更高,但 21 字符的 nanoid 仍提供 126 位熵,对几乎所有实际应用场景都足够。对于 URL slug 和二维码内容,nanoid 最短可以到 10–12 字符,在典型生成速率下碰撞概率才开始成为问题。避免对原始 UUID 进行 Base64 编码后截断——截断会破坏比特的统计独立性,使碰撞更难推理。
// 32-char hex string from UUID v4
const hexId = crypto.randomUUID().replaceAll('-', '');
// "3e7f1a924b0c4d8e9f127a6b3c8d5e1f"
// 21-char URL-safe ID via nanoid
import { nanoid } from 'nanoid';
const shortId = nanoid();
// "V1StGXR8_Z5jdHi6B-myT"相关工具
Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.
Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.