English | Русский | 简体中文 | Bahasa Indonesia
Генератор уникальных ID для JavaScript — лёгкий, безопасный, ID можно применять в URL.
«Поразительный уровень бессмысленного перфекционизма, который просто невозможно не уважать»
- Лёгкий. 130 байт (после минификации и gzip). Без зависимостей. Size Limit следит за размером.
- Быстрый. В 2 раза быстрее UUID.
- Безопасный. Использует аппаратный генератор случайных чисел. Можно использовать в кластерах машин.
- Короткие ID. Используется больший алфавит, чем у UUID (
A-Za-z0-9_-
). Поэтому длина ID уменьшена с 36 до 21 символа. - Работает везде. Nano ID уже портировали на 20 языков программирования.
import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"
Поддерживает современные браузеры, IE (с Babel), Node.js и React Native.
Nano ID похож на UUID v4 (случайный). У них сравнимое число битов случайности в ID (126 у Nano ID против 122 у UUID), поэтому они обладают похожей вероятностью возникновения коллизий (повторной генерации ранее выданных ID):
Чтобы вероятность повтора приблизилась к 1 на миллиард, нужно сгенерировать 103 триллиона ID.
Но между ними есть 3 важных отличия:
- Nano ID использует более широкий алфавит, и сравнимое количество битов случайности будут упакованы в более короткую строку (21 символ, против 36 у UUID).
- Код Nano ID в 4 раз меньше, чем у
uuid/v4
— 130 байт против 483. - Благодаря оптимизациям с выделением памяти, Nano ID в 2 раза быстрее UUID.
$ node ./test/benchmark.js
crypto.randomUUID 25,603,857 ops/sec
@napi-rs/uuid 9,973,819 ops/sec
uid/secure 8,234,798 ops/sec
@lukeed/uuid 7,464,706 ops/sec
nanoid 5,616,592 ops/sec
customAlphabet 3,115,207 ops/sec
uuid v4 1,535,753 ops/sec
secure-random-string 388,226 ops/sec
uid-safe.sync 363,489 ops/sec
cuid 187,343 ops/sec
shortid 45,758 ops/sec
Async:
nanoid/async 96,094 ops/sec
async customAlphabet 97,184 ops/sec
async secure-random-string 92,794 ops/sec
uid-safe 90,684 ops/sec
Non-secure:
uid 67,376,692 ops/sec
nanoid/non-secure 2,849,639 ops/sec
rndm 2,674,806 ops/sec
Среда сравнения: ThinkPad X1 Carbon Gen 9, Fedora 34, Node.js 16.10.
См. также хорошую статью о теориях генераторов случайных чисел: Secure random values (in Node.js)
-
Непредсказуемость. Вместо предсказуемого
Math.random()
, Nano ID использует модульcrypto
в Node.js и Web Crypto API в браузере. Эти модули дают доступ к аппаратному генератору случайных чисел. -
Равномерность. Например, существует популярная ошибка
random % alphabet
, которую часто допускают при разработке генератора ID. Распределение вероятности для каждого символа может не быть одинаковым. Из-за неравномерности использования пространства алфавита, на перебор ID потребуется меньше времени, чем ожидается. Nano ID использует более совершенный алгоритм, а равномерность распределения символов покрыта тестами. -
Документация: все хитрости Nano ID хорошо документированы — смотрите комментарии в исходниках.
-
Уязвимости: если вы нашли уязвимость в Nano ID, свяжитесь с командой безопасности Tidelift. Они проконтролируют исправление и проинформируют пользователей.
npm install --save nanoid
Для быстрого прототипирования вы можете подключить Nano ID с CDN без установки. Не используйте этот способ на реальном сайте, так как он сильно бьёт по скорости загрузки сайта.
import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js'
Nano ID поддерживает ES-модули. Вам не надо ничего делать, чтобы ES-импорты работали в webpack, Rollup, Parcel, или Node.js.
import { nanoid } from 'nanoid'
Для Node.js также поддерживается CommonJS-импорт:
const { nanoid } = require('nanoid')
Nano ID разделён на три модуля: стандартный (блокирующий), асинхронный и небезопасный.
По умолчанию используются символы, безопасные для URL (A-Za-z0-9_-
).
Длина ID по умолчанию — 21 символ
(чтобы вероятность коллизий была соизмеримой с UUID v4).
Безопасный и простой в использовании способ использования Nano ID.
Из-за особенностей работы генератора случайных чисел при использовании этого способа ЦПУ может иногда простаивать без работы.
import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"
Функция также принимает необязательный аргумент, задающий длину ID:
nanoid(10) //=> "IRFa-VaY2b"
При изменении размера, всегда проверяйте риски в нашем калькуляторе коллизий.
Для аппаратной генерации случайных чисел процессор накапливает электромагнитные шумы. Обычно они накоплены заранее, и получение случайных чисел происходит быстро. Но могут быть ситуации, когда системе требуется время на накопление энтропии.
При использовании синхронного API процесс заблокируется во время накопления энтропии. Например, веб-сервер не сможет обрабатывать запрос следующего посетителя, пока не сгенерирует ID для предыдущего.
Но если использовать асинхронный API у Nano ID, то процесс будет работать более эффективно: во время накопления шума сможет выполняться другая задача.
import { nanoid } from 'nanoid/async'
async function createUser() {
user.id = await nanoid()
}
Про ожидание накопления энтропии можно почитать в описании метода
crypto.randomBytes
в документации Node.js.
К сожалению, эта оптимизация имеет смысл только для Node.js. Web Crypto API в браузерах не имеет асинхронной версии.
По умолчанию, Nano ID использует аппаратный генератор случайных чисел для получения непредсказуемых ID и минимизации риска возникновения коллизий (повторной генерации ранее выданных ID). Но если вам не требуется устойчивость к подбору ID, то вы можете перейти на небезопасный генератор.
import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"
Но учтите, что предсказуемость ID может быть использована для атаки на систему.
Функция customAlphabet
позволяет создать свою функцию nanoid
с нужным вам алфавитом и длиной ID.
import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid() //=> "4f90d13a42"
import { customAlphabet } from 'nanoid/async'
const nanoid = customAlphabet('1234567890abcdef', 10)
async function createUser() {
user.id = await nanoid()
}
import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid()
Не забудьте проверить риски коллизии вашего алфавита и длины
на нашем калькуляторе. nanoid-dictionary
содержит много популярных
примеров альтернативных алфавитов.
Алфавит должен содержать ≤256 символов. Иначе мы не сможем гарантировать непредсказуемость ID.
Длину ID можно менять не только в customAlphabet()
, но и при вызове
генератора, который она вернёт:
import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid(5) //=> "f01a2"
Функция customRandom
позволяет создать свою функцию nanoid
со своими
генераторами случайных чисел, алфавитом и длинной ID.
Например, можно использовать генератор c seed для повторяемости тестов.
import { customRandom } from 'nanoid'
const rng = seedrandom(seed)
const nanoid = customRandom('abcdef', 10, size => {
return new Uint8Array(size).map(() => 256 * rng())
})
nanoid() //=> "fbaefaadeb"
Функция в третьем аргументе customRandom
должна принимать длину массива
и возвращать нужный массив со случайными числами
Если вы хотите заменить только генератор случайных чисел, но оставить
URL-совместимый алфавит, то стандартный алфавит доступен
в экспорте urlAlphabet
.
const { customRandom, urlAlphabet } = require('nanoid')
const nanoid = customRandom(urlAlphabet, 10, random)
У асинхронной и небезопасной версий нет customRandom
.
Если вам нужна поддержка IE, потребуется включить компиляцию node_modules
с помощью Babel и вручную убрать вендорный префикс у crypto
.
// polyfills.js
if (!window.crypto) {
window.crypto = window.msCrypto
}
import './polyfills.js'
import { nanoid } from 'nanoid'
Не используйте Nano ID для генерации свойства key
в JSX. При каждом рендере
key
будет разный, что плохо скажется на производительности.
function Todos({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={nanoid()}> /* НЕ ДЕЛАЙТЕ ТАК */
{todo.text}
</li>
))}
</ul>
)
}
Подробнее об использовании свойства key
читайте в
официальной документации React.
React Native не имеет встроенного аппаратного генератора случайных чисел. Полифил ниже работает в чистом React Native и в Expo начиная с версии 39.
- Прочитайте документацию
react-native-get-random-values
и установите его. - Импортируйте эту библиотеку до импорта Nano ID.
import 'react-native-get-random-values'
import { nanoid } from 'nanoid'
Для Rollup понадобятся плагины @rollup/plugin-node-resolve
.
plugins: [
nodeResolve({
browser: true
})
]
В PouchDB и CouchDB, ID не могут начинаться с _
. Добавьте к ID префикс,
так как иногда Nano ID может сгенерировать ID начинающийся с _
.
Изменить стандартный ID можно через следующую опцию:
db.put({
_id: 'id' + nanoid(),
…
})
const mySchema = new Schema({
_id: {
type: String,
default: () => nanoid()
}
})
Веб-воркеры не имеют доступа к аппаратному генератору случайных чисел.
Аппаратный генератор нужен, в том числе, для непредсказуемости ID. Например, когда доступ к секретному документу защищён ссылкой с уникальным ID.
Если вам не нужна непредсказуемость ID, то в Веб-воркере можно использовать небезопасный генератор ID.
import { nanoid } from 'nanoid/non-secure'
nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"
Можно сгенерировать уникальный ID прямо из терминала, вызвав npx nanoid
.
Для этого в системе должна быть только Node.js. npx
сама скачает Nano ID,
если его нет в системе.
$ npx nanoid
npx: installed 1 in 0.63s
LZfXLFzPPR4NNrgjlWDxn
Длину генерируемых ID можно передать в аргументе --size
(или -s
):
$ npx nanoid --size 10
L3til0JS4z
Изменить алфавит можно при помощи аргумента --alphabet
(ли -a
)
(в этом случае --size
обязателен):
$ npx nanoid --alphabet abc --size 15
bccbcabaabaccab
Nano ID был портирован на множество языков. Это полезно, чтобы сервер и клиент генерировали ID по одной схеме.
- C#
- C++
- Clojure и ClojureScript
- ColdFusion/CFML
- Crystal
- Dart и Flutter
- Deno
- Go
- Elixir
- Haskell
- Janet
- Java
- Nim
- Perl
- PHP
- Python со словарями
- R (со словарями)
- Ruby
- Rust
- Swift
Для остальных сред можно использовать Nano ID для терминала.
- Калькулятор длины ID поможет подобрать оптимальную длину ID, в зависимости от частоты выдачи ID и нужной надёжности системы.
nanoid-dictionary
с популярными алфавитами дляcustomAlphabet
.nanoid-good
гарантирует, что в случайном ID не будет матерных слов.