nanoid、randomstring、shortid 和 uuid 都是用於在 JavaScript 應用程式中生成唯一識別碼(ID)的工具,但各自針對不同使用場景進行優化。nanoid 強調短小、快速且加密安全;randomstring 提供高度可自訂的字串生成(如驗證碼),但預設不安全;shortid 已被官方棄用,不建議用於新專案;uuid 則實作標準化的通用唯一識別碼(UUID),廣泛用於需要跨系統相容性的場合。這些套件在安全性、ID 長度、格式彈性及維護狀態上有顯著差異,開發者需根據實際需求謹慎選擇。
在現代前端應用中,我們經常需要產生不重複的識別碼(ID)— 從臨時 UI 元件 key、本地快取索引,到客戶端預先建立的資料實體。nanoid、randomstring、shortid 和 uuid 是四個常見選擇,但它們設計目標、安全性和使用情境大不相同。本文將從實際工程角度深入剖析這些套件的技術細節與取捨。
nanoid 專注於「安全、簡短、快速」的隨機 ID 生成,使用加密安全的亂數來源(Web Crypto API),並針對 bundle size 和執行速度高度優化。適合需要高碰撞抗性且對長度敏感的場景(如 URL 縮網址、前端臨時 ID)。
randomstring 提供極高的客製化彈性,允許指定字元集、長度、甚至排除易混淆字元(如 0/O, l/1)。但預設不使用加密安全亂數,需手動啟用。適合需要特定格式(如密碼、驗證碼)而非純粹唯一 ID 的情境。
shortid 曾是輕量級 ID 生成熱門選擇,但官方已明確標示為棄用(deprecated),建議不要用於新專案。其演算法非加密安全,且存在可預測性問題。
uuid 實作 RFC4122 標準,支援 v1(時間戳 + MAC)、v4(隨機)、v5(命名空間雜湊)等多種版本。v4 是最常用選項,提供強大的唯一性保證,但 ID 長度固定為 36 字元(含連字號),較不適合對長度敏感的場景。
只有 nanoid 和 uuid (v4) 預設使用加密安全亂數(crypto.getRandomValues() 或 Node.js 的 crypto.randomBytes)。這對防止 ID 被預測至關重要 — 例如用於認證 token 或公開資源識別碼。
randomstring 預設使用 Math.random(),不具備加密安全性。若需安全亂數,必須明確設定 generator: 'crypto'。
shortid 使用非加密安全演算法,且因位數不足(7-14 字元),在高頻率生成下碰撞機率顯著上升。
// nanoid: 預設即安全
import { nanoid } from 'nanoid';
const id = nanoid(); // 例: "V1StGXR8_Z5jdHi6B-myT"
// randomstring: 需手動啟用安全模式
import randomString from 'randomstring';
const unsafe = randomString.generate(10); // 使用 Math.random()
const safe = randomString.generate({
length: 10,
generator: 'crypto' // 必須指定
});
// uuid: v4 預設安全
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4(); // 例: "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
// shortid: 不安全且已棄用
import shortid from 'shortid';
const id = shortid.generate(); // 例: "SJWvrXhPf" — 避免使用!
| 套件 | 預設長度 | 可自訂長度 | 可自訂字元集 | 固定格式 |
|---|---|---|---|---|
nanoid | 21 | ✅ | ✅(透過 alphabet) | ❌ |
randomstring | 32 | ✅ | ✅(完整控制) | ❌ |
shortid | 7-14(動態) | ⚠️(有限) | ❌ | ❌ |
uuid | 36(含 -) | ❌ | ❌ | ✅(RFC4122) |
nanoid 在預設 21 字元下,碰撞機率遠低於 UUID v4(因使用更大字元集:A-Za-z0-9_-)。若需更短 ID,可安全縮減長度:
import { customAlphabet } from 'nanoid';
const nanoidShort = customAlphabet('1234567890abcdef', 10);
const id = nanoidShort(); // 例: "a3f9c8e2b1"
randomstring 提供最細緻的格式控制,適合生成人類可讀 ID:
// 生成不含易混淆字元的 8 位驗證碼
const code = randomString.generate({
length: 8,
charset: 'alphanumeric',
capitalization: 'uppercase',
exclude: '0O1l'
}); // 例: "A2B4C6D8"
uuid 的格式嚴格遵循標準,若需移除連字號,需手動處理:
const compact = uuidv4().replace(/-/g, ''); // 32 字元無分隔符
雖然指令禁止提及具體數字,但從設計上:
nanoid 為最小化 bundle size 設計,核心邏輯極簡,適合對體積敏感的前端專案。uuid 因支援多版本(v1/v3/v4/v5),整體體積較大,但可 tree-shaking 僅引入 v4。randomstring 因功能豐富(多種生成器、字元集邏輯),體積相對較大。shortid 雖小,但因已棄用且不安全,不應考慮。根據 npm 頁面 與 GitHub 倉庫,shortid 自 2018 年起不再維護,作者明確建議遷移至 nanoid。其主要問題包括:
重要提醒:任何新專案都應避免使用
shortid。既有專案應規劃遷移。
nanoid// React 中生成唯一 key
{fields.map(field => (
<Input key={field.id || nanoid()} />
))}
nanoid(自訂字母表)或 uuid v4// 生成 12 位 URL 安全 ID
const shareId = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 12)();
randomstring// 生成 6 位數字驗證碼
const otp = randomString.generate({
length: 6,
charset: 'numeric'
});
uuid v4// 客戶端預先建立實體
const newPost = {
id: uuidv4(),
title: 'Hello'
};
| 需求 | 最佳選擇 |
|---|---|
| 最短、最快、安全的隨機 ID | nanoid |
| 完全自訂格式(如驗證碼) | randomstring(記得啟用 crypto) |
| 標準相容 UUID | uuid v4 |
| 任何新專案 | 絕對不要用 shortid |
記住:若你的 ID 會暴露給外部(如 URL、API 回應),務必使用加密安全亂數。nanoid 和 uuid v4 是前端最穩健的選擇;randomstring 僅在需要特殊格式時使用,且要小心設定安全選項。
選擇 uuid 若你需要符合 RFC4122 標準的通用唯一識別碼(特別是 v4 隨機版本),以確保與後端系統或其他服務的相容性。UUID v4 提供強大的唯一性保證和加密安全性,但 ID 長度固定(36 字元含連字號),不適合對長度敏感的場景。
選擇 nanoid 若你需要生成短小(預設 21 字元)、加密安全且高效能的隨機 ID。它特別適合前端臨時識別碼、URL 縮網址或任何對長度敏感又需高碰撞抗性的場景。其設計極簡,bundle size 小,且預設使用 Web Crypto API 保證安全性。
不要選擇 shortid — 此套件已由作者正式標示為棄用(deprecated),且存在安全性與碰撞風險問題。官方文件明確建議遷移至 nanoid。任何新專案都應避免使用,既有專案應規劃替換方案。
選擇 randomstring 若你需要高度自訂格式的字串(如排除易混淆字元的驗證碼、特定字元集的密碼),而非純粹的唯一 ID。注意:預設使用 Math.random() 不具備加密安全性,必須明確設定 generator: 'crypto' 才能用於敏感場景。
For the creation of RFC9562 (formerly RFC4122) UUIDs
crypto API for random valuesuuid command line utility[!NOTE]
Starting with
uuid@12CommonJS is no longer supported. See implications and motivation for details.
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 ...
uuid.NIL | The nil UUID string (all zeros) |
uuid.MAX | The 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 |
The nil UUID string (all zeros).
Example:
import { NIL as NIL_UUID } from 'uuid';
NIL_UUID; // ⇨ '00000000-0000-0000-0000-000000000000'
The max UUID string (all ones).
Example:
import { MAX as MAX_UUID } from 'uuid';
MAX_UUID; // ⇨ 'ffffffff-ffff-ffff-ffff-ffffffffffff'
Convert UUID string to array of bytes
str | A valid UUID String |
| returns | Uint8Array[16] |
| throws | TypeError if str is not a valid UUID |
[!NOTE] Ordering of values in the byte arrays used by
parse()andstringify()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
// ]
Convert array of bytes to UUID string
arr | Array-like collection of 16 values (starting from offset) between 0-255. |
[offset = 0] | Number Starting index in the Array |
| returns | String |
| throws | TypeError if a valid UUID string cannot be generated |
[!NOTE] Ordering of values in the byte arrays used by
parse()andstringify()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'
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 |
| returns | UUID String if no buffer is specified, otherwise returns buffer |
| throws | Error 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'
Convert a UUID from version 1 to version 6
import { v1ToV6 } from 'uuid';
v1ToV6('92f62d9e-22c4-11ef-97e9-325096b39f47'); // ⇨ '1ef22c49-2f62-6d9e-97e9-325096b39f47'
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."
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 |
| returns | UUID 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'
Create an RFC version 5 (namespace w/ SHA-1) UUID
name | String | Array |
namespace | String | 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 |
| returns | UUID String if no buffer is specified, otherwise returns buffer |
[!NOTE] The RFC
DNSandURLnamespaces are available asv5.DNSandv5.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'
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'
Convert a UUID from version 6 to version 1
import { v6ToV1 } from 'uuid';
v6ToV1('1ef22c49-2f62-6d9e-97e9-325096b39f47'); // ⇨ '92f62d9e-22c4-11ef-97e9-325096b39f47'
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 |
| returns | UUID String if no buffer is specified, otherwise returns buffer |
Example:
import { v7 as uuidv7 } from 'uuid';
uuidv7(); // ⇨ '01941f29-7c00-73e4-a310-744d2167fc5b'
"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. Thevalidate()andversion()methods do work with such UUIDs, however.
Test a string to see if it is a valid UUID
str | String to validate |
| returns | true 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
Detect RFC version of a UUID
str | A valid UUID String |
| returns | Number The RFC version of the UUID |
| throws | TypeError 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
0for theNILUUID, and15for theMAXUUID.
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 UUIDsPrior 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:
options: Internal state is utilized to improve UUID uniqueness.options: Internal state is NOT used and, instead, appropriate defaults are applied as needed.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
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-get-random-valuesuuid. 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