NanoID生成器

Generate tiny URL-safe unique IDs with customizable alphabet

字母表

长度

数量

点击"生成"以创建 NanoID

什么是 NanoID?

NanoID 是一个小巧、快速、URL 安全的随机 ID 生成器。默认情况下,它使用 64 字符字母表(A-Za-z0-9_-)生成 21 字符字符串,提供约 126 位熵——与 UUID v4 的 122 位相当,但字符串更短。

NanoID 不嵌入时间戳或任何结构化数据。每个 ID 都是纯随机的,由操作系统的加密安全随机数生成器生成(浏览器中的 crypto.getRandomValues(),Node.js 中的 crypto.randomBytes())。

NanoID vs UUID v4

NanoID 和 UUID v4 都是由 CSPRNG 支持的随机 ID 生成器。它们在格式、长度和生态系统支持方面有所不同:

属性NanoID(默认)UUID v4
格式URL 安全字母数字 + _-连字符十六进制
长度21 个字符(默认)36 个字符
约 126 位122 位
URL 安全是——无需编码是(含连字符)
字母表64 个字符(A-Za-z0-9_-)16 个字符(0-9a-f)
依赖项需要 npm 包原生(crypto.randomUUID)
可自定义是——长度和字母表
标准无(社区库)RFC 4122 / RFC 9562

当与外部系统的互操作性很重要时选择 UUID v4——具有原生 UUID 列的数据库、期望 UUID 格式的 API,或解析 UUID 的日志基础设施。当你想要更短的 ID 并控制整个技术栈时选择 NanoID。

按大小划分的碰撞概率

NanoID 的碰撞概率取决于 ID 长度和生成速率。下表使用默认的 64 字符字母表:

大小(字符)可能的 ID 数量碰撞安全性
664约 1 / 45 亿——适合几千个 ID
864约 1 / 4.5 万亿——适合数百万个 ID
1164约 1 / 2.8 千万亿——适合数十亿个 ID
1664约 1 / 1.2 × 10^19——适合数万亿个 ID
2164约 1 / 10^30——适合每天 1000 亿个 ID,持续几百年
3264与 UUID v4 相当(122 位)
3636超过 UUID v4

默认 21 字符大小的选择是为了在更短 41% 的同时匹配 UUID v4 的抗碰撞性(约 126 位)。对于大多数应用,21 个字符是正确的选择。

自定义字母表

NanoID 的字母表完全可自定义。该库接受任何唯一字符字符串作为字母表,并仅使用这些字符生成 ID:

纯数字A-Za-z0-9_-
使用 '0123456789' 生成全数字 ID——适用于短信验证码或 PIN 风格的标识符。
小写十六进制A-Za-z0-9
使用 '0123456789abcdef' 生成没有 UUID 连字符格式的紧凑十六进制字符串。
人类可读0-9a-f
排除视觉上模糊的字符(0、O、1、I、l),用于用户可能需要手动输入的 ID。
自定义域0-9
使用任何适合你应用程序的字符集——例如,仅使用元音 + 辅音生成可发音的 ID。

重要:仅对非安全敏感的应用程序使用 nanoid/non-secure(例如 UI 元素 ID)。对于任何需要不可猜测的 ID,始终使用默认的安全导入。

NanoID 如何生成随机性

NanoID 使用操作系统的加密安全伪随机数生成器(CSPRNG)。在浏览器中是 crypto.getRandomValues();在 Node.js 中是 crypto.randomFillSync()。这是用于 TLS 会话密钥的相同熵源——远比 Math.random() 更强。

拒绝采样(避免模运算偏差)

一种生成随机字符的朴素方法是:取一个随机字节(0–255)并计算 byte % alphabetSize。当字母表大小不能整除 256 时,这会引入模运算偏差——某些字符出现的频率略高于其他字符。

NanoID 使用拒绝采样消除这种偏差:

  1. 确定覆盖字母表大小的最小 2 的幂掩码(例如对于 64 字符字母表,掩码为 63 = 0b00111111)
  2. 生成随机字节并应用掩码:byte & mask
  3. 如果掩码后的值在字母表范围内,则使用它。否则,丢弃并重试。

这意味着一些随机字节被丢弃,但结果是在字母表上均匀分布——没有任何字符比其他字符更可能出现。

How the algorithm works — step by step
// Pure browser — no npm package needed
function generateNanoid(alphabet, size) {
  const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
  const step = Math.ceil((1.6 * mask * size) / alphabet.length)
  let id = ''
  while (id.length < size) {
    const bytes = crypto.getRandomValues(new Uint8Array(step))
    for (const byte of bytes) {
      const idx = byte & mask
      if (idx < alphabet.length) {
        id += alphabet[idx]
        if (id.length === size) break
      }
    }
  }
  return id
}

const URL_SAFE = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
generateNanoid(URL_SAFE, 21)  // → "V1StGXR8_Z5jdHi6B-myT"

环境支持

Browser
现代浏览器(Chrome 37+、Firefox 34+、Safari 7+)——使用 crypto.getRandomValues()
Node.js 14+
Node.js 14.18+——使用 crypto.randomFillSync()
Deno
Deno——使用 crypto.getRandomValues()
Bun
React Native——使用 expo-crypto 或 react-native-get-random-values polyfill
Edge / Cloudflare Workers
边缘运行时(Cloudflare Workers、Vercel Edge)——Web Crypto API 可用
React Native
Bun——原生加密支持

代码示例

JavaScript / TypeScript

JavaScript
// npm i nanoid
import { nanoid } from 'nanoid'
nanoid()          // → "V1StGXR8_Z5jdHi6B-myT" (21 chars, URL-safe)
nanoid(8)         // → "Uakgb_J5" (custom size)

// Custom alphabet
import { customAlphabet } from 'nanoid'
const hexId  = customAlphabet('0123456789abcdef', 16)
hexId()       // → "4f3a1b8c9d2e0f7a"

const numId  = customAlphabet('0123456789', 8)
numId()       // → "30812894"

浏览器(CDN)

NanoID 可以通过 CDN 导入在浏览器中直接使用。无需构建步骤即可快速原型开发。

JavaScript
// Pure browser — no npm package needed
function generateNanoid(alphabet, size) {
  const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
  const step = Math.ceil((1.6 * mask * size) / alphabet.length)
  let id = ''
  while (id.length < size) {
    const bytes = crypto.getRandomValues(new Uint8Array(step))
    for (const byte of bytes) {
      const idx = byte & mask
      if (idx < alphabet.length) {
        id += alphabet[idx]
        if (id.length === size) break
      }
    }
  }
  return id
}

const URL_SAFE = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
generateNanoid(URL_SAFE, 21)  // → "V1StGXR8_Z5jdHi6B-myT"

Python

Python
# pip install nanoid
from nanoid import generate

generate()              # → "V1StGXR8_Z5jdHi6B-myT"
generate(size=8)        # → "Uakgb_J5"
generate('0123456789abcdef', 16)  # custom alphabet + size

Node.js(CommonJS)

JavaScript
// Node.js — stdlib only, no npm needed
const { randomFillSync } = require('crypto')

function nanoid(alphabet, size) {
  const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
  const step = Math.ceil((1.6 * mask * size) / alphabet.length)
  let id = ''
  while (id.length < size) {
    const bytes = randomFillSync(Buffer.alloc(step))
    for (const byte of bytes) {
      const idx = byte & mask
      if (idx < alphabet.length) { id += alphabet[idx]; if (id.length === size) break }
    }
  }
  return id
}

常见问题

NanoID 是加密安全的吗?
是的——使用默认导入时,NanoID 使用操作系统级 CSPRNG 生成 ID(浏览器中的 crypto.getRandomValues,Node.js 中的 crypto.randomFillSync)。这是用于密钥生成的相同熵源。NanoID 值适合用作会话令牌、API 密钥和其他安全敏感的标识符。
我可以使用 NanoID 作为数据库主键吗?
可以。NanoID 字符串是 URL 安全的,可以存储为 CHAR 或 VARCHAR 列。但是,NanoID 没有时间戳组件,所以 ID 不能按生成顺序排序——这将在高插入速率下导致 B 树索引碎片化,类似于 UUID v4。对于按时间排序的主键,请改用 ULID 或 UUID v7。
NanoID 与 crypto.randomUUID() 相比如何?
两者都使用 CSPRNG 并提供强随机性。crypto.randomUUID() 是原生的(无依赖),生成 36 字符连字符 UUID v4 字符串,并被数据库和 API 普遍识别。NanoID 需要 npm 包但生成更短的字符串(默认 21 个字符),字母表可自定义。在大多数情况下,优先选择 crypto.randomUUID() 以避免依赖。
如果我使用非常短的 NanoID 会发生什么?
短 ID(例如 6–8 个字符)的碰撞概率显著更高。默认 64 字符字母表的 6 字符 NanoID 只有约 680 亿个可能值——适合在碰撞风险变得不可忽视之前的几千个 ID。使用上面的碰撞概率表为你的预期 ID 量选择合适的大小。
我可以在没有 npm 的浏览器中使用 NanoID 吗?
可以。NanoID 支持通过 CDN 进行 ESM 导入(例如 jsDelivr 或 esm.sh)。在带有 type="module" 的脚本标签中将其作为模块导入。这对于快速原型开发很有用,但不推荐用于生产——固定到特定版本并考虑自托管脚本。
NanoID 在所有环境中都可以工作吗?
NanoID 在所有现代浏览器、Node.js、Deno、Bun、Cloudflare Workers 和 Vercel Edge Functions 中都可以工作。对于 React Native,需要 getRandomValues 的 polyfill(react-native-get-random-values)。该库设计为环境无关,并自动检测可用的 crypto API。