只有踩过坑才懂:前端生成唯一 ID,别用 Date.now ()了!试试它crypto.randomUUID()

前言

有些时候,我们需要生成唯一 ID

看上去很简单的样子,但真要做到百分百不重复,可比想的要坑。

第一个坑:拿时间戳加随机数凑数

很多小伙伴的第一反应可能会这样来写

复制代码
function backUniqueIDvalue() {
  return Date.now().toString(36) + Math.random().toString(36).substr(2);
}

简单解释一下这段代码的意思

Date.now().toString(36)

Date.now():获取当前的时间戳(精确到毫秒的数字)。

toString(36):将这个时间戳数字转换为 36 进制的字符串。

36 进制包含 0-9 和 a-z(10数字+26个字母)。

这样可以让原本很长的数字时间戳变得非常短,且包含字母

简单解释一下这段代码的意思

Math.random().toString(36).substr(2)

Math.random():生成一个 0 到 1 之间的随机小数(例如 0.123456789)。

.toString(36):同样把这个小数转换为 36 进制字符串(例如 "0.4fzyo82mvyr")。

.substr(2):截取从第 2 位开始的字符串,目的是去掉开头的 "0.",只保留后面的随机字符部分。

举个例子:

如果当前时间戳转 36 进制是 "lxixn9",

随机数转 36 进制是 "0.abc123"

那么最终拼接的结果就是类似 "lxixn9abc123" 这样的一串短 ID

可能会出现的问题

看着好像是没有问题的,而且代码也挺简单的。

但是实际上放到生产环境中,这就是个定时炸弹

时间戳精度不够

Date.now()只能精确到毫秒,要是在同一毫秒里连续调用两次这个函数。

那么就会返回相同的结果,此时 ID 的前半段就会完全一样

我们都知道 Math.random()这玩意生成的不是真正的随机数,只是伪随机数。

运气不好的时候,在较短时间内可能跑出一模一样的序列

说到这里,小伙伴们可能已经知道,生成的 ID 唯一性可能不是那么高,可能会重复的。

第二个坑:用自增的计数器

还其他小伙伴想的更简单:整个全局的数字。

每次生成 ID 就把数字加 1,从 0 开始往上排不就得了?

但是如果用户开了俩同款页面,每个页面的计数器都是从零开始算,生成的 ID 会直接重复。

显然,这样的办法不太行。

这个时候,今天我们的主角就要闪亮登场了。

掌声有请:crypto.randomUUID()

下面我们简单介绍一下我们的专家大拿

crypto.randomUUID() 是 ES2023标准 API

crypto.randomUUID() 是 ES2023标准 API.

ES2023(即 ECMAScript 2023)是在 2023年 正式推出的。

crypto.randomUUID() 浏览器的版本要求

浏览器环境:Chrome 92+、Firefox 95+、Safari 15.4+。

该 API 只能在安全上下文(即 HTTPS 或 localhost)中使用(重要)。

判断浏览器是否支持 crypto.randomUUID()

判断是否支持crypto.randomUUID()。

使用 typeof 检查typeof是否是 undefined

然后使用 typeof 检查crypto.randomUUID是否是一个函数

如果都成立的话,说明支持

复制代码
// 兼容性写法
let uniqueId;
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
  // 现代环境直接使用原生 API
  uniqueId = crypto.randomUUID();
  // 生成的是:96e74085-38de-46bf-ba25-f85fe1bafc9d 这种格式的字符串
} else {
  // 旧环境降级方案(例如引入 uuid 第三方库)
  // uniqueId = uuid.v4(); 
  console.warn('当前环境不支持 crypto.randomUUID');
}

为啥说这个是王者呢?

即使你以每秒生成 10 亿 个 UUID 的速度,持续生成 100 年。

发生一次重复的概率依然低于 10⁻³⁶(即 1 后面跟着 36 个零)。

换句话说,你需要生成大约 2.71 万亿个id,才会有十亿分之一的概率发生一次重复。

小伙伴们是不是觉得很厉害呢?

crypto.randomUUID()需要注意的点

大家应该看到了这样一句话:

该 API 只能在安全上下文(即 HTTPS 或 localhost)中使用。

换一句话说:

本地开发时:使用 localhost 或 127.0.0.1 访问,它是可以正常工作的。

如果你的网页是通过普通的 http:// 协议

(例如 http://192.168.1.10http://xxx.com)访问的

浏览器就会出于安全考虑,直接禁用这个 API。此时调用它。

控制台通常会报错 TypeError: crypto.randomUUID is not a function。

crypto.randomUUID()也是不想象中的那样强大

本来以为是瑶瑶领先同行的。结果还隐藏着安全上下文的限制

其他生成唯一 id 第三方库 uuid 库

安装:npm install uuid

使用:

复制代码
import { v4 as uuidv4 } from 'uuid'; 
const id = uuidv4();

其他的办法生成唯一 id

我旁边机智的小伙伴说:这样写也可以

复制代码
Math.random() + Math.random()+ Math.random()+ Math.random()

实在绷不住了,哈哈