← 文章 开发感想

「纬书」八字命盘演算功能修复技术报告

技术报告:修复八字命盘工具 WASM 加载失败问题,涉及 Cloudflare CDN 压缩冲突排查与边缘计算解压方案。

術數工具

基于 WebAssembly & 原生 iOS,探索星历之美

1. 问题背景

在将「纬书」玄学博客的八字命盘工具(WASM 版)重构为统一的“帛书”视觉风格并部署至 Cloudflare Workers 后,用户反馈前端页面点击“推算命盘”后出现报错:演算失败: 无法加载 WASM 文件。同时,控制台打印出魔数匹配错误:expected magic word 00 61 73 m, found cf ff ff 7f

该问题导致核心的八字排盘功能完全失效。本报告详细记录了该问题的排查过程、根本原因分析以及最终的修复方案。


2. 故障诊断与原因分析

经过对前端加载逻辑、网络请求抓包以及 WebAssembly 实例化过程的深入排查,发现该故障并非单一原因引起,而是由三个独立的技术问题叠加导致的。

2.1 压缩格式冲突与 CDN 拦截(核心问题)

现象: 前端尝试通过 fetch 获取 ganzhi.wasm.gz(18MB)文件,并使用 DecompressionStream('gzip') 进行解压。然而,解压后传递给 WebAssembly.instantiate 的字节流前 8 字节为 cf ff ff 7f,而非标准的 WASM 魔数 00 61 73 6d

分析: cf ff ff 7f 是 Brotli 压缩算法的文件头标识。进一步抓包发现,尽管前端请求的是 .gz 文件,但由于浏览器在请求时自动携带了 Accept-Encoding: gzip, deflate, br 请求头,Cloudflare CDN 边缘节点检测到客户端支持 Brotli,便主动将原始的 Gzip 文件再次使用 Brotli 进行了压缩,并返回了 Content-Encoding: br

这就导致前端 DecompressionStream('gzip') 接收到的是 Brotli 格式的数据,解压失败,进而导致 WebAssembly 引擎抛出魔数不匹配的致命错误。

2.2 JSON 数据解析路径异常

现象: 在解决 WASM 加载问题后,前端控制台显示计算已完成,但页面上的四柱、五行、大运等 UI 元素依然显示为占位符“—”。

分析: 原始的 JS 胶水代码在处理 WASM 返回的 JSON 字符串时,直接访问了 data.pillars 等顶层属性。然而,最新编译的 WASM 模块返回的数据结构被包裹在了一个 result 对象中,实际结构为 {"result": {"pillars": {...}}, "success": true}。由于路径错误,前端渲染函数接收到了 undefined,导致 UI 无法更新。

2.3 字段格式不兼容

现象: 四柱的干支数据未能正确填入对应的 DOM 节点。

分析: 旧版渲染逻辑期望 WASM 返回分离的天干和地支字段(如 yearStem: "庚", yearBranch: "午")。但实际返回的数据是将干支合并的单字符串(如 year: "庚午")。这导致前端解构赋值失败。


3. 修复方案与实施

针对上述三个问题,我们采取了“服务端解压 + 前端适配”的综合修复策略。

3.1 边缘计算层(Cloudflare Workers)重构

为了彻底避开浏览器与 CDN 之间的自动压缩内容协商(Content Negotiation)冲突,我们放弃了在前端使用 DecompressionStream 解压的方案,转而利用 Cloudflare Workers 的边缘计算能力。

具体实现: 我们编写了一个拦截脚本 worker.js,当检测到对 /dist/ganzhi.wasm 的请求时,Worker 会在服务端读取静态资产中的 ganzhi.wasm.gz 文件,在边缘节点完成 Gzip 解压,并强制设置 Content-Type: application/wasm,然后直接将纯净的原始 WASM 字节流返回给前端。

这一方案不仅解决了双重压缩的冲突,还利用了边缘节点的算力,减轻了客户端浏览器的解压负担。

3.2 前端渲染逻辑修复

针对 JSON 结构和字段格式的变化,我们对 index.html 中的 renderResult 函数进行了重写。

修复项旧逻辑新逻辑
数据层级const p = data.pillars;const data = raw.result ? raw.result : raw; const p = data.pillars;
四柱拆分[p.yearStem, p.yearBranch]const sb = p.year; $(sId).textContent = sb[0]; $(bId).textContent = sb.slice(1);
五行渲染依赖特定中文字符键值增加中英文字典映射,支持动态生成带有相应 CSS 颜色类的 HTML 标签

3.3 视觉风格统一

在修复功能的同时,我们将八字命盘的 UI 样式与「纬书」博客主站进行了深度统一。包括引入白纸底色(#FAF7F2)、朱砂红强调色(#C0392B)以及墨迹黑文字(#1A1A1A),并在顶部添加了统一的返回导航栏,确保了整体品牌视觉的连贯性。


4. 总结

本次修复成功解决了 Cloudflare CDN 自动压缩机制与 WebAssembly 加载之间的冲突问题。通过将解压逻辑前置到 Workers 边缘节点,并重构前端数据解析路径,八字命盘工具现已恢复稳定运行。

该案例也为后续在 Serverless 架构中部署大型 WASM 文件提供了宝贵的经验:在处理非标准二进制资产时,应警惕 CDN 默认的透明压缩行为,必要时需通过自定义 Worker 脚本进行显式干预。