WebAssembly進階系列三:微信小程序支持webP的WebAssembly方案

導語:相信很多人據說過 WebAssembly,它是由 Google、Microsoft、Mozilla、Apple 等幾家大公司合做發起的一個關於面向Web的通用二進制和文本格式的項目。如今就讓咱們一步步揭開WebAssembly的神祕面紗,並親自動手將WebAssembly應用在實際業務中。javascript

1. 引言

經過《WebAssembly進階系列一:WebAssembly是什麼》《WebAssembly進階系列二:WebAssembly處於編譯階段哪一個環節》這兩篇文章深刻了解WebAssembly的由來、優點及適用場景後,接下來即是實踐檢驗真理的時候,讓咱們一塊兒動手將WebAssembly應用在微信小程序場景中,讓微信小程序環境支持解碼webP格式(不瞭解或沒聽過webP的各位同仁,請先移步到「探究WebP的一些事兒」)。html

2. WebAssembly工做流程

動手以前,讓咱們先來了解下如何加載和運行WebAssembly的代碼:C / C++ / Rust / Java等高級語言開發的代碼或功能庫 -> Emscripten編譯 -> wasm文件 -> 結合WebAssembly JS API -> 瀏覽器環境中運行,以下圖所示:前端

簡單來講,編譯前端LLVM / Emscripten流程能夠得到wasm文件和膠水js。而後,經過膠水js來加載wasm並轉爲arrayBuffer格式。緊接着進行編譯和實例化後,便可用JavaScript與WebAssembly通訊。java

詳細過程以及每一個過程調用的API以下圖所示:python

3. 瀏覽器環境支持webP

瞭解完WebAssembly的工做流程後,是否是還不清楚要從哪開始搞起?你能夠去github官網上看一下libwebp開源項目,Google已經徹底支持把libwebp源碼編譯爲wasm和asm.js兩個版本了。針對不支持WebAssembly的系統或不兼容WebAssembly的瀏覽器,能夠在損失一點性能的狀況降低級爲使用asm.js。具體編譯步驟以下圖所示:git

待編譯完以後,咱們即可得到wasm文件和膠水JS。而後,咱們能夠用「python -m SimpleHTTPServer 8080」啓動一個本地服務,在瀏覽器地址欄輸入 http://localhost:8080 後就能看到webP解碼後的圖片。github

最後,讓咱們來總結下整個流程。web

(1)用LLVM / Emscripten / CMake工具對libwebp解碼庫進行編譯,得到wasm文件和膠水JS。canvas

(2)膠水JS申請內存,對wasm文件進行編譯、加載和實例化後,導出Module對象。小程序

(3)利用Module對象上的WebpToSDL方法對webP進行解碼,並轉成Canvas在瀏覽器渲染顯示出來,呈現最終的圖片。

4. 微信小程序環境支持webP

微信小程序在Android / iOS上用於執行腳本以及渲染組件的環境都不盡相同。

在Android上,微信小程序邏輯層的JavaScript代碼運行在V8中,視圖層是由自研XWeb引擎基於Mobile Chrome 67內核來渲染,自然支持webP格式;在iOS上,微信小程序邏輯層的JavaScript代碼運行在JavaScriptCore中,視圖層是由WKWebView來渲染,宿主Safari瀏覽器內核不支持webP格式。

經過第3節內容,咱們知道瀏覽器環境已經可以支持webP了,那直接把以前編譯好的wasm文件和膠水JS扔進微信小程序的運行環境,而後跑起來不就搞定了?Too young too simple!

瀏覽器環境支持webP的思路是libwebp解碼webP -> jpg / png / gif的canvas圖片渲染顯示,這已經改變了原來image組件的結構。

而微信小程序提供給開發者的組件不容許去改變它原來的結構,所以換種思路是libwebp解碼webP -> jpg / png / gif的rgb data -> jpg / png / gif base64 -> 回傳給JS並賦值給image src進行渲染顯示。

下面我羅列下從libwebp編譯wasm文件和膠水JS開始,直到在微信小程序環境跑通爲止,整個過程當中遇到的一些坑點和優化點:

(1)編譯CMakeLists.txt時需加上「-O3」選項,大大提高編譯速度。

(2)編譯CMakeLists.txt時需加上「-s USE_PTHREADS=0」選項,由於iOS Safari瀏覽器不兼容ShareArrayBuffer共享緩衝區。

(3)編譯CMakeLists.txt時需加上「-s ALLOW_MEMORY_GROWTH=1」選項,目的是爲了解決解碼超大分辨率的webP圖片時出現的OOM問題。

(4)因爲微信小程序環境的兼容性問題,去除膠水JS代碼中libwebp編譯時加上的SDL相關代碼,能節省100KB左右的空間。

(5)去除膠水JS中ENVIRONMENT_IS_NODE / ENVIRONMENT_IS_SHELL相關的代碼,由於微信小程序環境並未使用到。

(6)因爲iOS Safari瀏覽器的兼容性問題,將膠水JS中流式編譯和實例化的方法去掉,替換成非流式編譯和實例化的方法。

(7)因爲WebAssembly尚未和<script type='module'>或ES6的import語句集成,所以將wasm文件先轉成base64字符串。等膠水JS運行加載邏輯時,再將base64轉成ArrayBuffer並編譯和實例化後導出Module對象,節省從服務器下載wasm文件的時間。

(8)編譯CMakeLists.txt時需加上「-s USE_LIBPNG=1」選項編譯libpng.a庫,而後將webP解碼得到的rgb數據,經過png解碼庫轉成png內存數據,緊接着轉成base64回傳給JS,最後賦值給image src進行渲染顯示。難點是rgb轉成png內存數據這一步出了點問題,可是wasm沒法調試代碼,只能經過搭建libpng的VS工程進行斷點調試,最終定位到是rgb轉png data時傳入的data_size爲0致使。

(9)膠水JS裏的new WebAssembly.Memory代碼在微信小程序環境運行時,會報「refused to create a webassembly object without 'unsafe-eval'」的錯誤,必須在page-frame.html裏的CSP設置里加上unsafe-eval才能解決。

踩了這麼多坑以後,終於能在微信小程序環境裏支持webP了。實測WebAssembly在解碼不一樣格式不一樣分辨率的webP時,性能都完勝JavaScript。

5. 寫在最後

雖然WebAssembly的解碼性能比JavaScript快很多,但遇到超大分辨率(如1920 x 1080等)的webP時,卻遠遠落後於客戶端的解碼性能。綜合對比各類方案的性能和兼容性以後,咱們仍是採用了基於iOS客戶端自定義協議webphttps的方案,大體步驟以下:

(1)首先,微信小程序基礎庫判斷開發者在image組件使用的是webP格式時,則在image src里加上webp頭部如webpexample.png

(2)而後,客戶端經過NSURLProtocol協議挾持webphttps的請求,並下載相應的webP數據進行解碼。

(3)最後,再把解碼後的image數據回吐給瀏覽器進行渲染顯示。

到最後,咱們完成了微信小程序環境支持webP的方案落地,敬請期待

參考資料

  1. webassembly介紹
  2. 加載和運行WebAssembly代碼
  3. WebAssembly在企業郵箱中的一次實踐
  4. Download and install — Emscripten 1.38.38 documentation
  5. 探究WebP的一些事兒
  6. libwebp開源項目

我的公衆號:前端開發升值記

相關文章
相關標籤/搜索