← 文章 開發感想

「緯書」八字命盤演算功能修復技術報告

技術報告:修復八字命盤工具 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 腳本進行顯式幹預。