惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
aimingoo的专栏
aimingoo的专栏
WordPress大学
WordPress大学
G
Google Developers Blog
博客园 - 叶小钗
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
J
Java Code Geeks
U
Unit 42
云风的 BLOG
云风的 BLOG
阮一峰的网络日志
阮一峰的网络日志
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
D
Docker
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
V
V2EX
T
Tailwind CSS Blog

蚊子的前端博客

微说 | 有的人觉得只要不考XX,一定能考好!-蚊子的前端博客 微说 | 春天来了-蚊子的前端博客 明天和意外不知道哪个先来-蚊子的前端博客 微说 | 既然错过了路口,就及时止损,重新规划路线上路-蚊子的前端博客 微说 | 2025年的出生人口数是792万-蚊子的前端博客 微说 | 工作年终总结,让写对接的接口的数量?-蚊子的前端博客 微说 | 温家宝:没有政治体制改革的成功 经济体制改革不可能进行到底-蚊子的前端博客 微说 | 违法犯罪了该不该被禁言?-蚊子的前端博客 微说 | 不明白为什么在推荐去俄罗斯旅游-蚊子的前端博客 微说 | 易中天论骗子-蚊子的前端博客 微说 | 一场大风吹散了秋天-蚊子的前端博客 微说 | 既要、又要、还要、更要!-蚊子的前端博客 又是一年的国庆雨季-蚊子的前端博客 微说 | 预估下2025年的出生人口数据-蚊子的前端博客 微说 | 可惜了我那些小时候的书本-蚊子的前端博客 微说 | 牛马有的是,驴不够了!-蚊子的前端博客 微说 | 能否有一条非户口也能高考的路-蚊子的前端博客 微说 | 小聊中医-蚊子的前端博客 微说 | 将要制作的一款新产品-蚊子的前端博客 微说 | 做人要有信-蚊子的前端博客 微说 | 好一个正义联盟-蚊子的前端博客 微说 | 都是见过吃过的主儿-蚊子的前端博客 微说 | 追求8小时工作制有错吗?-蚊子的前端博客 微说 | 如果尖锐的批评完全消失-蚊子的前端博客 微说 | 很好!-蚊子的前端博客 微说 | 封禁用户可以,但要告知具体原因-蚊子的前端博客 微说 | 面朝大海,春暖花开-蚊子的前端博客 微说 | 程序员的悲哀是什么?-蚊子的前端博客 微说 | 2025年出生人口的预测-蚊子的前端博客 前端在 LiveKit 中如何获取所有的参与者-蚊子的前端博客
自己封装 uuid 踩的一个小坑-蚊子的前端博客
author · 2025-08-01 · via 蚊子的前端博客

在项目中,需要生成一个全局唯一的 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 时,使用其他的字符来代替中横线。