Wenzi

自己封装 uuid 踩的一个小坑

蚊子前端博客
发布于 2025/08/01 10:04
若封装的功能采用了多种策略,最好输出的结果格式是保持一致的!

在项目中,需要生成一个全局唯一的 uuid,来标记每行数据。我就在想,只是全局不唯一就行了,也懒得加载第三方 npm 包了,自己小小封装一下。

结果没想到踩了一个坑,测试环境是好好地,到了正式环境就报错了。我起初还以为是 webpack 打包差异造成的,最后排查下来,是我封装的 uuid() 有问题。

我最最开始,是用时间戳+自增数来生成唯一 id 的。后来觉得这种实现方式不太优雅,而且浏览器也支持了 crypto.randomUUID() 方法,我就想着给改写一下。同时,这个方法只在 localhost 或者 https 环境下才可用,然后我也做了下兜底兼容。

export const uuid = () => {
  if (typeof window.crypto?.randomUUID === "function") {
    return window.crypto.randomUUID();
  }
  if (typeof window.crypto.getRandomValues === "function" && typeof window.Uint8Array === "function") {
    const arr = new Uint8Array(16);
    crypto.getRandomValues(arr);
    let str = "";

    arr.forEach((item) => {
      str += item.toString(16).padStart(2, "0");
    });
    return str;
  }
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
    const r = (Math.random() * 16) | 0,
      v = c == "x" ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
};

上面封装的代码,其实有一个问题:这三种方式生成的唯一 id 的格式是不一样的。

我们本地的开发环境和测试环境,都是 http 协议的,以浏览器的兼容性,都会都到第 2 步里,也没发现有啥问题。然后上线后,出问题了,以生成的 uuid 来找对应的数据,一直找不到。

最后发现是两个原因造成的:

  1. 线上正式环境是 https 协议,所以会走第 1 步,生成一个 uuid;
  2. 这三种方式生成的唯一 id 的格式是不一样的(我刚开始想的是只要全局唯一即可,没必要非得是标准的 8-4-4-4-12 的这种格式);
  3. 业务逻辑中是用[name]-[uuid]拼接成的 key,然后我再用-来分割出 name 和 uuid;

分割的代码如下:

const [name, uuid] = key.split("-");

本地开发和测试环境中都是在第 2 步中生成的唯一 id,这里面没有中横线-的分隔符,一直没问题。但在正式环境中,是用第 1 步的方法生成的唯一 id,所以这里面就会有中横线-的分隔符,就会导致分割的时候出错。取到的 uuid 是不完整的。

这里也给自己一个警示:

若采用了多种策略来封装一个功能,一定要注意输出数据的一致性。

知道原因后,就知道该怎么改了,要么就去掉 uuid 中所有的中横线-的分隔符,要么就在业务中拼接 key 时,使用其他的字符来代替中横线。

标签:fe
阅读(161)
No data
No data