Skip to content

指南 · 基础

本篇按库讲清各自的机制与常用用法,把「会用」升级到「懂为什么」。7 个库相互独立,可按需跳读。

一、mitt:极简的代价与收益

mitt 返回的 Emitter 只有 on / off / emit 三个方法和一个 all 属性。它的设计哲学是「能砍就砍」:

  • allMap:键为事件名(或 '*'),值为 handler 数组。它对外暴露,可直接操作,例如 bus.all.clear() 一次性清空所有监听。
  • 方法不依赖 this:可以安全解构使用,const { on, emit } = mitt() 后单独调用也没问题。
  • 通配 '*'on('*', (type, payload) => ...) 监听所有事件,回调首参是被触发的事件名。
  • 触发顺序:同一次 emit先调用该事件名的类型化 handler,再调用 '*' 通配 handler
ts
const bus = mitt();
bus.on("foo", () => console.log("typed"));
bus.on("*", (t) => console.log("wild:", t));
bus.emit("foo", 1); // 依次输出 "typed" 与 "wild: foo"

mitt 没有 once

要实现一次性监听,得在 handler 内部解绑自己:

ts
function onceHandler(payload: unknown) {
  // ...处理逻辑
  bus.off("ready", onceHandler); // 用完即解绑
}
bus.on("ready", onceHandler);

二、qs:嵌套、数组与安全限制

嵌套与数组

qs 用方括号路径表达层级:a[b][c]=d{ a: { b: { c: 'd' } } }。数组解析支持 a[]=x(隐式)与 a[0]=x(显式下标)。

序列化的数组格式(arrayFormat)

取值输出({ a: ['b','c'] }encode:false
indices默认a[0]=b&a[1]=c
bracketsa[]=b&a[]=c
repeata=b&a=c
commaa=b,c

默认的安全限制(防 DoS)

qs 的几个默认值都是为防止恶意构造的超大/超深查询拖垮服务

选项默认含义
depth5最大嵌套深度,超出部分并入键名
parameterLimit1000最大参数(键值对)数,超出默认忽略
arrayLimit20数组下标超过它就转成对象(避免稀疏大数组)
ts
qs.parse("a[25]=x"); // { a: { '25': 'x' } }(下标 25 > 20,转对象)

这正是 qs 优于原生 URLSearchParams 的地方:后者只能处理扁平键值对,且无这些保护。

三、JSZip:异步 I/O 与类型选择

JSZip 的核心 I/O 都是异步、返回 Promise

  • zip.generateAsync({ type }) —— 生成压缩包;
  • JSZip.loadAsync(data) —— 读取已有 zip;
  • zipObject.async(type) —— 读出单个文件内容。

type 取值要按环境选:

type适用
blob浏览器下载 / 预览
nodebufferNode 写文件(fs.writeFile
base64嵌入 Data URL / 传输
uint8array / arraybuffer二进制处理
ts
// 浏览器:生成并下载
const blob = await zip.generateAsync({ type: "blob" });
saveAs(blob, "out.zip"); // 配合 FileSaver

// Node:写盘
const buf = await zip.generateAsync({ type: "nodebuffer" });
fs.writeFileSync("out.zip", buf);

遍历条目用 zip.forEach((path, entry) => ...) 或直接读 zip.files(路径 → ZipObject 的对象)。

四、FileSaver.js:它只做「保存」这一步

saveAs(blob, filename, options?) 的底层套路:用 URL.createObjectURL(blob) 造临时 URL,创建带 download 属性的隐藏 <a> 并程序化点击,再释放 URL,并对各浏览器/iOS 做兼容回退。

  • 首参可以是 Blob / File / URL 字符串:URL 同源走 <a download>,跨源需服务端 CORS 支持。
  • autoBom:当 blob 的 typecharset=utf-8 时,加 UTF-8 BOM,帮助 Excel 等正确识别编码、避免中文乱码(导出 CSV 常用)。
  • 它不产生内容:内容由 new Blob([...])canvas.toBlob、JSZip 等准备。
ts
canvas.toBlob((blob) => saveAs(blob!, "shot.png")); // canvas → 图片下载

五、qrcode:四种输出 + 纠错级别

qrcode 按「输出目标」提供四组方法:

方法输出环境
toCanvas画到 canvas 元素浏览器 / Node
toDataURLbase64 Data URL(可赋 img.src浏览器 / Node
toString字符串(svg / utf8 / terminal浏览器 / Node
toFile写文件(png / svg / utf8仅 Node

纠错级别 errorCorrectionLevel(默认 M):

级别可恢复污损特点
L~7%数据密度最高、最脆弱
M~15%默认,均衡
Q~25%较抗损
H~30%最抗损(可遮挡更多,常用于带 logo 的码)

所有渲染方法都同时支持「回调」与「Promise」:传 callback 即回调式,不传则返回 Promise。

六、chroma.js:构造、转换与色阶

构造与转换

chroma(input) 接受 hex、CSS 名、chroma.rgb(...)chroma.hsl(...) 等多种输入;再链式取目标表示:.rgb() / .hsl() / .lab() / .lch() / .hex() / .css()。调整类方法 .darken() / .brighten() / .saturate() / .alpha() 都返回新的 chroma 对象,可继续链式。

色阶 scale

chroma.scale([colors]) 返回一个色阶函数,传 0~1 取插值色:

ts
const s = chroma.scale(["white", "red"]);
s(0.5).hex(); // 中间色
s.colors(5); // 取 5 个等距颜色
s.mode("lab"); // 改插值色彩空间(默认 'rgb')

注意默认插值 mode 的差异:chroma.scale 默认 rgb,而 chroma.mix / chroma.average 默认 lrgb(线性 RGB)。

七、marked:转换与安全模型

marked.parse(md)(或 marked(md))把 Markdown 编译成 HTML,默认同步返回字符串。常用选项:

选项默认含义
gfmtrueGitHub Flavored Markdown(表格、删除线、任务列表等)
breaksfalse把单个换行渲染为 <br>
pedanticfalse严格遵循原始 markdown.pl
asyncfalse开启后 parse 返回 Promise<string>

marked 默认不净化 HTML

marked 会原样输出内嵌的 HTML。旧的 sanitize / sanitizer 选项已被移除。渲染不可信内容时,必须自行用 DOMPurify 净化输出:

ts
const html = DOMPurify.sanitize(marked.parse(userInput) as string);
element.innerHTML = html; // 净化必须在写入 DOM 之前

进入 指南 · 进阶:mitt 类型化与解绑模式、qs 高级选项、JSZip 流式与压缩级别、chroma 色阶分级、marked 自定义渲染。