从"复制粘贴到抓狂"到"30分钟上线一个工具":我用 AI 做了个大小写转换器

20 阅读6分钟

前两周写文档的时候遇到一个很烦的问题:从 API 返回的数据里拿了一堆 user_namecreated_at 这样的字段,要转成前端用的 userNamecreatedAt 驼峰命名。手动改吧,十几二十个字段,一个个改又慢又容易漏;写个脚本吧,就为了这一锤子买卖,感觉又有点小题大做。

后来想想,其实这种"临时但高频"的小需求,与其每次现写脚本,不如直接做个在线工具,一劳永逸。于是就有了这个文本大小写转换器。整个过程用 AI 辅助开发,从需求到上线大概花了 30 分钟,这篇文章就来聊聊实现思路和一些踩坑记录。

为什么不做成脚本,非要做网页?

说实话,一开始我的第一反应也是写个 Node.js 脚本,跑一下就行。但仔细一想,有几个问题:

  1. 不可复用:下次遇到类似需求,脚本找不找得到是一回事,找到还得改参数。
  2. 不方便分享:同事遇到同样的问题,把脚本发给他?他可能连 Node 环境都没装。
  3. 不够直观:脚本只能处理静态输入,没法实时预览,交互性为零。

所以还是决定做成一个纯前端的单文件 HTML 工具。零依赖、无构建、双击就能用,扔到服务器上就是一个页面。

用 AI 写代码:需求描述比代码本身更重要

既然决定用 AI 辅助开发,那第一步就是给它清晰的规格说明。这里我踩了个大坑:一开始我只说"帮我写个大小写转换工具",结果 AI 给了一个极其简陋的版本——一个输入框,一个按钮,点一下才转换,而且只有 UPPER 和 lower 两种格式。

这显然不是我要的。于是我开始反思 prompt 的写法。后来我把需求细化成这样:

你是一名资深前端工程师。请根据下方规格,生成一个独立的单文件 HTML 工具。
- 生产级别代码,质量高,可读性强
- 严格实现「功能列表」中的所有功能,不得省略、不得用 placeholder 代替
- 所有用户可见文案必须使用 i18n 机制

然后附上完整的功能列表、UI 布局 ASCII 图、交互细节、技术要求和验收标准。关键是把"实时预览""每种格式独立复制按钮""i18n"这些非功能需求也写清楚

这个 prompt 的效果立竿见影,AI 生成的代码基本能跑,但有几个问题:

AI 生成的代码,哪里不对?

问题 1:中文文本被硬生生"分词"了

AI 第一版用的是 text.split(' ') 来分词,然后做驼峰转换。这在纯英文场景没问题,但一旦输入包含中文(比如 "你好 world"),结果就变成了 你好world,中文和英文之间没有空格分隔,看起来非常别扭。

我的解决方案是让 AI 用 Unicode 属性正则来分词,配合驼峰边界识别:

function splitWords(s) {
  return String(s)
    .replace(/([\p{Ll}\p{N}])(\p{Lu})/gu, '$1 $2')       // helloWorld -> hello World
    .replace(/(\p{Lu}+)(\p{Lu}\p{Ll})/gu, '$1 $2')        // parseXMLDoc -> parse XML Doc
    .split(/[^\p{L}\p{N}]+/u)
    .filter(Boolean);
}

这个正则用到了 \p{L}(字母)、\p{N}(数字)和 Unicode 属性转义,配合 u 标志,能正确处理中文、带音标的拉丁字母(比如 é、ü)等非 ASCII 字符。中文等 CJK 字符不会被拆分,因为它们本身不是大小写敏感的

问题 2:复制功能在非 HTTPS 环境下静默失败

AI 用的是 navigator.clipboard.writeText(),这在 localhost 或 HTTPS 环境下没问题,但如果部署在纯 HTTP 的服务器上(比如一些内网环境),这个 API 会直接报错。

我让 AI 加了一个降级方案:如果 navigator.clipboard 不存在,就回退到 document.execCommand('copy') 配合临时 textarea。虽然 execCommand 已经废弃了,但作为 fallback 它仍然是最可靠的方案,毕竟"又不是不能用"。

function copyText(btn) {
  const text = btn.getAttribute('data-result');
  const flash = msg => {
    const orig = t('copy');
    btn.textContent = msg;
    setTimeout(() => btn.textContent = orig, 1500);
  };
  if (!navigator.clipboard) {
    flash(t('copyFail'));
    return;
  }
  navigator.clipboard.writeText(text)
    .then(() => flash(t('copied')))
    .catch(() => flash(t('copyFail')));
}

问题 3:XSS 漏洞

AI 第一版在渲染结果时直接用了 innerHTML 拼接字符串,如果用户输入包含 <img src=x onerror=alert(1)> 这样的内容,就会执行恶意脚本。这是个很典型的 XSS 漏洞。

我让 AI 加了一个 escapeHtml 函数,对输出内容做转义:

function escapeHtml(s) {
  const div = document.createElement('div');
  div.textContent = s;
  return div.innerHTML.replace(/"/g, '&quot;');
}

问题 4:i18n 文案没配全

AI 生成的 i18n 字典里漏了"复制失败"的提示,而且中英文文案的对应关系有些对不上。我逐条核对了一遍,确保每个 key 在 zhen 两个语言包中都有值。

和 AI 协作的几点心得

  1. prompt 要像写需求文档一样:把功能列表、UI 布局、交互细节、验收标准都写清楚,AI 才能生成符合预期的代码。你给它的信息越模糊,它给你的代码越"能用但不好用"。
  2. 代码评审不能省:AI 生成的代码看起来能跑,但安全性和健壮性需要人工把关。特别是 XSS、输入校验、错误处理这些方面,AI 往往考虑得不够周全。
  3. 迭代式对话比一次性生成更有效:我大概和 AI 来回改了四五轮,每一轮聚焦一个问题(分词、复制、转义、i18n),这样比一次性让它生成完美代码要快得多。

技术细节:不只是大小写转换那么简单

这个工具的核心是一个 transforms 数组,每个元素定义了一个转换函数:

const transforms = [
  { key: 'upper', fn: s => s.toUpperCase() },
  { key: 'lower', fn: s => s.toLowerCase() },
  { key: 'camelCase', fn: s => splitWords(s).map((w, i) => i === 0 ? w.toLowerCase() : capWord(w)).join('') },
  { key: 'pascalCase', fn: s => splitWords(s).map(capWord).join('') },
  // ... 其余格式
];

其中 splitWords 是核心:它先把驼峰和缩写边界拆开(parseXMLDoc -> parse XML Doc),再按非字母数字字符分割。这样无论是 hello worldhello_worldhello-world 还是 helloWorld 作为输入,都能正确转换成目标格式。

性能优化:由于是实时预览,每次输入都会触发所有转换函数。对于一般文本量(几千字以内)完全没问题,但如果要处理十几万字的文本,可以考虑用 requestAnimationFrame 做节流。不过这个场景下"又不是不能用",所以就没做。

适用场景

这个工具适合:

  • 日常开发:把 API 返回的 snake_case 字段转成 camelCase,或反过来
  • 数据库字段映射:把数据库列名转成实体类属性名
  • 配置文件处理:把环境变量名转成其他命名风格
  • 文档整理:把全大写的标题转成 Title Case

结语

这只是一个很简单的工具,但它的意义在于:用 AI 辅助开发,把"临时脚本"变成"可复用的在线工具",成本其实很低。如果你也有类似的文本处理需求,可以直接在线体验:craftvo.app/zh/tool/cas…

所有转换都在浏览器本地完成,不会上传任何数据,代码也是开源的,可以直接扒下来改。