纯前端工具页的几个实现细节
目录
工具页的逻辑看起来都很简单,但每个都有几处不按直觉走的地方。记录几个实际遇到的。
btoa 处理不了中文
btoa 只接受每个字符码位在 0-255 范围内的字符串,传中文会直接抛 InvalidCharacterError:
btoa('你好'); // InvalidCharacterError
正确做法是先用 TextEncoder 转成字节,再逐字节交给 btoa:
function encodeBase64(text) {
const bytes = new TextEncoder().encode(text);
let binary = '';
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary);
}
解码时反向走一遍,用 TextDecoder 还原。注意别用 String.fromCharCode(...bytes) 展开大数组——参数过多会栈溢出,长文本上必翻车。
Web Crypto 没有 MD5
crypto.subtle.digest 支持 SHA-1 / SHA-256 / SHA-384 / SHA-512,但不支持 MD5,而且这是规范层面的有意取舍,不是浏览器没实现。
MD5 早就不该用于安全场景,但校验文件完整性、对接老系统时还是会碰到。只能自己实现或引第三方库。于是同一个工具页里会出现两条代码路径:MD5 走库,SHA 系列走原生 API。
另外 crypto.subtle 只在安全上下文(HTTPS 或 localhost)下可用。本地用 IP 访问开发服务器时它是 undefined,这个坑很容易误判成代码写错了。
剪贴板要准备退路
navigator.clipboard.writeText 同样要求安全上下文,而且可能被权限策略拒绝。退路是老掉牙的 execCommand('copy'):
try {
await navigator.clipboard.writeText(text);
} catch {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
}
execCommand 已经废弃,但目前没有浏览器真的移除它,作为兜底仍然可靠。几个细节:临时 textarea 必须真的插进文档才能 select(),并且要 position: fixed 配合透明,否则页面会闪一下跳动。
JSON 错误要换算成行列
JSON.parse 抛出的 SyntaxError 只告诉你字符偏移量:
Unexpected token } in JSON at position 42
对着几百行的 JSON,position 42 没什么用。换算成行列号只要几行:
const before = text.slice(0, pos);
const line = before.split('\n').length;
const column = pos - before.lastIndexOf('\n');
不同引擎的报错文案并不统一(V8、SpiderMonkey、JavaScriptCore 各写各的),所以要用正则去抓 position,抓不到就原样展示消息,别假设格式固定。
时间戳的位数判断
时间戳输入框要同时接受秒和毫秒。按位数猜是最省事的办法:10 位当秒,13 位当毫秒。
更稳的判断是看数值范围——小于 10^11 的基本都是秒级(10^11 秒已经是公元 5138 年),超过的按毫秒处理。这样粘贴进来的时间戳带不带引号、有没有多余空格都不影响判断。