uuid vs nanoid vs randomstring vs shortid
Web 应用中的唯一标识符生成方案
uuidnanoidrandomstringshortid类似的npm包:

Web 应用中的唯一标识符生成方案

这些库用于在 JavaScript 环境中生成唯一字符串。nanoid 是现代首选,兼顾安全与体积。uuid 提供标准 RFC 格式,适合系统间互通。shortid 已废弃,存在安全风险。randomstring 适合生成临时令牌,但安全性需自行确认。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
uuid399,942,44115,34565.7 kB822 天前MIT
nanoid360,043,58727,11814.7 kB21 个月前MIT
randomstring1,088,62452217 kB21 个月前MIT
shortid05,70921.7 kB162 年前MIT

Web 应用中的唯一标识符生成方案:nanoid vs uuid vs shortid vs randomstring

在前端开发中,生成唯一标识符(ID)是常见需求,无论是用于列表渲染的 key、临时会话令牌,还是资源追踪。nanoid、uuid、shortid 和 randomstring 都是流行的选择,但它们在安全性、格式标准和维护状态上有显著差异。让我们从架构师的角度深入比较这些方案。

🔒 安全性与随机算法

生成 ID 的核心在于随机性。如果随机性不足,ID 可能被预测,导致安全漏洞。

nanoid 默认使用加密安全的随机生成器。

  • 在 Node.js 中使用 crypto 模块。
  • 在浏览器中使用 getRandomValues。
  • 确保 ID 难以被猜测。
// nanoid: 默认安全
import { nanoid } from 'nanoid';
const id = nanoid(); // 默认 21 字符,安全随机

uuid (v4) 也使用加密安全的随机数。

  • 符合 RFC 4122 标准。
  • 安全性高,但格式固定。
// uuid: v4 安全
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4(); // 标准 UUID 格式

shortid 存在已知安全问题。

  • 随机算法不够强,曾被报告可预测。
  • 已不再维护,不建议用于安全敏感场景。
// shortid: 不推荐
import shortid from 'shortid';
const id = shortid.generate(); // 存在安全风险

randomstring 安全性取决于环境。

  • Node.js 中通常使用 crypto。
  • 浏览器中可能回退到 Math.random(不安全)。
  • 需仔细检查文档配置。
// randomstring: 需确认环境
import randomstring from 'randomstring';
const id = randomstring.generate(); // 默认可能不安全

📏 格式与长度控制

不同的业务场景对 ID 的长度和字符集有不同要求。

nanoid 生成 URL 友好的字符串。

  • 默认 21 字符,紧凑。
  • 只包含字母、数字、_ 和 -。
  • 可自定义长度和字符集。
// nanoid: 自定义长度
import { nanoid } from 'nanoid';
const id = nanoid(10); // 10 字符

uuid 格式固定且较长。

  • 标准格式为 36 字符(包含连字符)。
  • 适合需要标准格式的系统交互。
  • 不易手动输入或阅读。
// uuid: 固定格式
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4(); // 36 字符,如 550e8400-e29b...

shortid 生成短字符串。

  • 长度不固定,通常较短。
  • 字符集包含特殊符号,可能不适合 URL。
// shortid: 短但不固定
import shortid from 'shortid';
const id = shortid.generate(); // 长度可变

randomstring 提供高度自定义。

  • 可指定字符集(如仅数字、仅大写)。
  • 适合生成验证码或临时密码。
// randomstring: 自定义字符集
import randomstring from 'randomstring';
const id = randomstring.generate({ length: 10, charset: 'numeric' });

📦 环境与打包体积

前端项目对 bundle 大小敏感,库的体积直接影响加载性能。

nanoid 体积极小。

  • 压缩后仅几百字节。
  • 无依赖,开箱即用。
  • 支持 ESM 和 CJS。
// nanoid: 轻量导入
import { nanoid } from 'nanoid';

uuid 体积相对较大。

  • 包含多种版本实现(v1, v4, v5 等)。
  • 可通过 tree-shaking 优化,但仍比 nanoid 大。
// uuid: 按需导入
import { v4 as uuidv4 } from 'uuid';

shortid 体积中等。

  • 已不再优化,依赖旧版构建工具。
  • 在新构建系统中可能产生警告。
// shortid: 旧式导入
import shortid from 'shortid';

randomstring 体积较小。

  • 功能单一,无复杂依赖。
  • 适合简单脚本或临时工具。
// randomstring: 简单导入
import randomstring from 'randomstring';

⚠️ 维护状态与风险

选择库时,维护状态决定了未来的技术债务。

nanoid 活跃维护。

  • 社区活跃,更新频繁。
  • 作者推荐作为 shortid 的替代品。

uuid 稳定维护。

  • 行业标准,长期支持。
  • 适合长期项目。

shortid 已废弃。

  • npm 页面明确标记为 Deprecated。
  • 仓库归档,不再接受修复。
  • 切勿在新项目中使用。

randomstring 维护较少。

  • 功能稳定但更新慢。
  • 适合非核心功能。

📊 总结对比表

特性nanoiduuidshortidrandomstring
安全性✅ 加密安全✅ 加密安全 (v4)❌ 存在风险⚠️ 依赖环境
格式URL 友好RFC 4122 标准短字符串高度自定义
体积极小中等中等小
维护状态✅ 活跃✅ 稳定❌ 已废弃⚠️ 较少更新
推荐场景默认首选系统互通无 (已废弃)临时令牌/密码

💡 最终建议

nanoid 是现代前端开发的默认选择 — 它安全、小巧且易于使用。除非你有特殊需求,否则应优先使用它。

uuid 仅在需要与后端系统严格匹配标准格式时使用。它的通用性值得额外的体积开销。

shortid 必须避免 — 它已废弃且不安全。迁移到 nanoid 只需几分钟,但能消除长期风险。

randomstring 适合特定场景 — 如生成仅数字的验证码。但在生成用户 ID 或会话令牌时,请谨慎评估其安全性。

核心原则:ID 生成看似简单,却关乎应用安全。选择经过验证、 actively maintained 的库 — 不要为了微小的便利牺牲安全性。

如何选择: uuid vs nanoid vs randomstring vs shortid

  • uuid:

    当后端系统或数据库强制要求 RFC 4122 标准格式时使用。虽然体积较大,但通用性最强,适合分布式系统追踪或需要标准格式的场景。

  • nanoid:

    如果你的项目需要安全、简短且 URL 友好的 ID,这是默认选择。它在浏览器和 Node.js 中都能保持 cryptographically secure,且体积极小。适合大多数前端场景,如列表键值或临时资源 ID。

  • randomstring:

    适用于生成非安全敏感的临时令牌或密码,且需要高度自定义字符集时。如果不需要标准格式且 nanoid 的字符集不满足需求,可以考虑它,但需注意浏览器端的安全性。

  • shortid:

    不要在的新项目中使用此包。它已正式废弃且存在已知安全漏洞,作者明确建议迁移至 nanoid。继续使用会带来维护风险和潜在的安全隐患。

uuid的README

uuid CI Browser

For the creation of RFC9562 (formerly RFC4122) UUIDs

[!NOTE]

Starting with uuid@12 CommonJS is no longer supported. See implications and motivation for details.

Quickstart

1. Install

npm install uuid

2. Create a UUID

import { v4 as uuidv4 } from 'uuid';

uuidv4(); // ⇨ 'b18794e8-5d0d-417c-b361-ba38e78411b4'

For timestamp UUIDs, namespace UUIDs, and other options read on ...

API Summary

uuid.NILThe nil UUID string (all zeros)
uuid.MAXThe max UUID string (all ones)
uuid.parse()Convert UUID string to array of bytes
uuid.stringify()Convert array of bytes to UUID string
uuid.v1()Generate a version 1 (timestamp) UUID
uuid.v1ToV6()Convert a version 1 UUID to version 6
uuid.v3()Generate a version 3 (namespace w/ MD5) UUID
uuid.v4()Generate a version 4 (random) UUID
uuid.v5()Generate a version 5 (namespace w/ SHA-1) UUID
uuid.v6()Generate a version 6 (timestamp, reordered) UUID
uuid.v6ToV1()Convert a version 6 UUID to version 1
uuid.v7()Generate a version 7 (Unix Epoch time-based) UUID
uuid.v8()"Intentionally left blank"
uuid.validate()Test a string to see if it is a valid UUID
uuid.version()Detect RFC version of a UUID

API

uuid.NIL

The nil UUID string (all zeros).

Example:

import { NIL as NIL_UUID } from 'uuid';

NIL_UUID; // ⇨ '00000000-0000-0000-0000-000000000000'

uuid.MAX

The max UUID string (all ones).

Example:

import { MAX as MAX_UUID } from 'uuid';

MAX_UUID; // ⇨ 'ffffffff-ffff-ffff-ffff-ffffffffffff'

uuid.parse(str)

Convert UUID string to array of bytes

strA valid UUID String
returnsUint8Array[16]
throwsTypeError if str is not a valid UUID

[!NOTE] Ordering of values in the byte arrays used by parse() and stringify() follows the left ↠ right order of hex-pairs in UUID strings. As shown in the example below.

Example:

import { parse as uuidParse } from 'uuid';

// Parse a UUID
uuidParse('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // ⇨
// Uint8Array(16) [
//   110, 192, 189, 127,  17,
//   192,  67, 218, 151,  94,
//    42, 138, 217, 235, 174,
//    11
// ]

uuid.stringify(arr[, offset])

Convert array of bytes to UUID string

arrArray-like collection of 16 values (starting from offset) between 0-255.
[offset = 0]Number Starting index in the Array
returnsString
throwsTypeError if a valid UUID string cannot be generated

[!NOTE] Ordering of values in the byte arrays used by parse() and stringify() follows the left ↠ right order of hex-pairs in UUID strings. As shown in the example below.

Example:

import { stringify as uuidStringify } from 'uuid';

const uuidBytes = Uint8Array.of(
  0x6e,
  0xc0,
  0xbd,
  0x7f,
  0x11,
  0xc0,
  0x43,
  0xda,
  0x97,
  0x5e,
  0x2a,
  0x8a,
  0xd9,
  0xeb,
  0xae,
  0x0b
);

uuidStringify(uuidBytes); // ⇨ '6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'

uuid.v1([options[, buffer[, offset]]])

Create an RFC version 1 (timestamp) UUID

[options]Object with one or more of the following properties:
[options.node = (random) ]RFC "node" field as an Array[6] of byte values (per 4.1.6)
[options.clockseq = (random)]RFC "clock sequence" as a Number between 0 - 0x3fff
[options.msecs = (current time)]RFC "timestamp" field (Number of milliseconds, unix epoch)
[options.nsecs = 0]RFC "timestamp" field (Number of nanoseconds to add to msecs, should be 0-10,000)
[options.random = (random)]Array of 16 random bytes (0-255) used to generate other fields, above
[options.rng]Alternative to options.random, a Function that returns an Array of 16 random bytes (0-255)
[buffer]Uint8Array or Uint8Array subtype (e.g. Node.js Buffer). If provided, binary UUID is written into the array, starting at offset
[offset = 0]Number Index to start writing UUID bytes in buffer
returnsUUID String if no buffer is specified, otherwise returns buffer
throwsError if more than 10M UUIDs/sec are requested

Example:

import { v1 as uuidv1 } from 'uuid';

uuidv1(); // ⇨ '57fd0000-c7d3-11ef-841d-514d2167fc5b'

Example using options:

import { v1 as uuidv1 } from 'uuid';

const options = {
  node: Uint8Array.of(0x01, 0x23, 0x45, 0x67, 0x89, 0xab),
  clockseq: 0x1234,
  msecs: new Date('2011-11-01').getTime(),
  nsecs: 5678,
};
uuidv1(options); // ⇨ '710b962e-041c-11e1-9234-0123456789ab'

uuid.v1ToV6(uuid)

Convert a UUID from version 1 to version 6

import { v1ToV6 } from 'uuid';

v1ToV6('92f62d9e-22c4-11ef-97e9-325096b39f47'); // ⇨ '1ef22c49-2f62-6d9e-97e9-325096b39f47'

uuid.v3(name, namespace[, buffer[, offset]])

Create an RFC version 3 (namespace w/ MD5) UUID

API is identical to v5(), but uses "v3" instead.

[!IMPORTANT] Per the RFC, "If backward compatibility is not an issue, SHA-1 [Version 5] is preferred."

uuid.v4([options[, buffer[, offset]]])

Create an RFC version 4 (random) UUID

[options]Object with one or more of the following properties:
[options.random]Array of 16 random bytes (0-255)
[options.rng]Alternative to options.random, a Function that returns an Array of 16 random bytes (0-255)
[buffer]Uint8Array or Uint8Array subtype (e.g. Node.js Buffer). If provided, binary UUID is written into the array, starting at offset
[offset = 0]Number Index to start writing UUID bytes in buffer
returnsUUID String if no buffer is specified, otherwise returns buffer

Example:

import { v4 as uuidv4 } from 'uuid';

uuidv4(); // ⇨ 'b18794e8-5d0d-417c-b361-ba38e78411b4'

Example using predefined random values:

import { v4 as uuidv4 } from 'uuid';

const v4options = {
  random: Uint8Array.of(
    0x10,
    0x91,
    0x56,
    0xbe,
    0xc4,
    0xfb,
    0xc1,
    0xea,
    0x71,
    0xb4,
    0xef,
    0xe1,
    0x67,
    0x1c,
    0x58,
    0x36
  ),
};
uuidv4(v4options); // ⇨ '109156be-c4fb-41ea-b1b4-efe1671c5836'

uuid.v5(name, namespace[, buffer[, offset]])

Create an RFC version 5 (namespace w/ SHA-1) UUID

nameString | Array
namespaceString | Array[16] Namespace UUID
[buffer]Uint8Array or Uint8Array subtype (e.g. Node.js Buffer). If provided, binary UUID is written into the array, starting at offset
[offset = 0]Number Index to start writing UUID bytes in buffer
returnsUUID String if no buffer is specified, otherwise returns buffer

[!NOTE] The RFC DNS and URL namespaces are available as v5.DNS and v5.URL.

Example with custom namespace:

import { v5 as uuidv5 } from 'uuid';

// Define a custom namespace.  Readers, create your own using something like
// https://www.uuidgenerator.net/
const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341';

uuidv5('Hello, World!', MY_NAMESPACE); // ⇨ '630eb68f-e0fa-5ecc-887a-7c7a62614681'

Example with RFC URL namespace:

import { v5 as uuidv5 } from 'uuid';

uuidv5('https://www.w3.org/', uuidv5.URL); // ⇨ 'c106a26a-21bb-5538-8bf2-57095d1976c1'

uuid.v6([options[, buffer[, offset]]])

Create an RFC version 6 (timestamp, reordered) UUID

This method takes the same arguments as uuid.v1().

import { v6 as uuidv6 } from 'uuid';

uuidv6(); // ⇨ '1efc7d35-7fd0-6000-841d-504d2167fc5b'

Example using options:

import { v6 as uuidv6 } from 'uuid';

const options = {
  node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
  clockseq: 0x1234,
  msecs: new Date('2011-11-01').getTime(),
  nsecs: 5678,
};
uuidv6(options); // ⇨ '1e1041c7-10b9-662e-9234-0123456789ab'

uuid.v6ToV1(uuid)

Convert a UUID from version 6 to version 1

import { v6ToV1 } from 'uuid';

v6ToV1('1ef22c49-2f62-6d9e-97e9-325096b39f47'); // ⇨ '92f62d9e-22c4-11ef-97e9-325096b39f47'

uuid.v7([options[, buffer[, offset]]])

Create an RFC version 7 (random) UUID

[options]Object with one or more of the following properties:
[options.msecs = (current time)]RFC "timestamp" field (Number of milliseconds, unix epoch)
[options.random = (random)]Array of 16 random bytes (0-255) used to generate other fields, above
[options.rng]Alternative to options.random, a Function that returns an Array of 16 random bytes (0-255)
[options.seq = (random)]32-bit sequence Number between 0 - 0xffffffff. This may be provided to help ensure uniqueness for UUIDs generated within the same millisecond time interval. Default = random value.
[buffer]Uint8Array or Uint8Array subtype (e.g. Node.js Buffer). If provided, binary UUID is written into the array, starting at offset
[offset = 0]Number Index to start writing UUID bytes in buffer
returnsUUID String if no buffer is specified, otherwise returns buffer

Example:

import { v7 as uuidv7 } from 'uuid';

uuidv7(); // ⇨ '01941f29-7c00-73e4-a310-744d2167fc5b'

uuid.v8()

"Intentionally left blank"

[!NOTE] Version 8 (experimental) UUIDs are "for experimental or vendor-specific use cases". The RFC does not define a creation algorithm for them, which is why this package does not offer a v8() method. The validate() and version() methods do work with such UUIDs, however.

uuid.validate(str)

Test a string to see if it is a valid UUID

strString to validate
returnstrue if string is a valid UUID, false otherwise

Example:

import { validate as uuidValidate } from 'uuid';

uuidValidate('not a UUID'); // ⇨ false
uuidValidate('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // ⇨ true

Using validate and version together it is possible to do per-version validation, e.g. validate for only v4 UUIds.

import { version as uuidVersion } from 'uuid';
import { validate as uuidValidate } from 'uuid';

function uuidValidateV4(uuid) {
  return uuidValidate(uuid) && uuidVersion(uuid) === 4;
}

const v1Uuid = 'd9428888-122b-11e1-b85c-61cd3cbb3210';
const v4Uuid = '109156be-c4fb-41ea-b1b4-efe1671c5836';

uuidValidateV4(v4Uuid); // ⇨ true
uuidValidateV4(v1Uuid); // ⇨ false

uuid.version(str)

Detect RFC version of a UUID

strA valid UUID String
returnsNumber The RFC version of the UUID
throwsTypeError if str is not a valid UUID

Example:

import { version as uuidVersion } from 'uuid';

uuidVersion('45637ec4-c85f-11ea-87d0-0242ac130003'); // ⇨ 1
uuidVersion('6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b'); // ⇨ 4

[!NOTE] This method returns 0 for the NIL UUID, and 15 for the MAX UUID.

Command Line

UUIDs can be generated from the command line using uuid.

$ npx uuid
ddeb27fb-d9a0-4624-be4d-4615062daed4

The default is to generate version 4 UUIDS, however the other versions are supported. Type uuid --help for details:

$ npx uuid --help

Usage:
  uuid
  uuid v1
  uuid v3 <name> <namespace uuid>
  uuid v4
  uuid v5 <name> <namespace uuid>
  uuid v7
  uuid --help

Note: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs
defined by RFC9562

options Handling for Timestamp UUIDs

Prior to uuid@11, it was possible for options state to interfere with the internal state used to ensure uniqueness of timestamp-based UUIDs (the v1(), v6(), and v7() methods). Starting with uuid@11, this issue has been addressed by using the presence of the options argument as a flag to select between two possible behaviors:

  • Without options: Internal state is utilized to improve UUID uniqueness.
  • With options: Internal state is NOT used and, instead, appropriate defaults are applied as needed.

Support

Browsers: uuid builds are tested against the latest version of desktop Chrome, Safari, Firefox, and Edge. Mobile versions of these same browsers are expected to work but aren't currently tested.

Node: uuid builds are tested against node (LTS releases), plus one prior. E.g. At the time of this writing node@20 is the "maintenance" release and node@24 is the "current" release, so uuid supports node@20-node@24.

Typescript: TS versions released within the past two years are supported. source

Known issues

"getRandomValues() not supported"

This error occurs in environments where the standard crypto.getRandomValues() API is not supported. This issue can be resolved by adding an appropriate polyfill:

React Native / Expo

  1. Install react-native-get-random-values
  2. Import it before uuid. Since uuid might also appear as a transitive dependency of some other imports it's safest to just import react-native-get-random-values as the very first thing in your entry point:
import 'react-native-get-random-values';
import { v4 as uuidv4 } from 'uuid';

Generated from README_js.md by runmd