微信小程序依靠微信巨大的用戶流量優勢,吸引來了眾多第三方開發者。幾乎每個公司都會想通過小程序拉到新的用戶群體。3月份接到公司任務,需要將公司各業務線小程序整合到一個小程序中。但擺在面前的問題是空間完全不夠。當時微信小程序的使用空間是 1024KB(現在已升為 2M),各個業務線獨立的小程序都不小,『機票火車票汽車票』小程序更是超過 900KB。如何整合和壓縮才能使 size 達標,成了最大的挑戰。
我們經過考慮,準備從兩個方面進行整合和壓縮,一是通過工程化的方式實現代碼復用——提取公共業務邏輯,公共組件;二是通過工具進行自定義化代碼打包壓縮處理。
工程規范制定
為了節省開發時間,我們盡量整合現有業務線小程序代碼,減少業務改動。
目錄結構
同時為了后期計算各業務占用空間情況方便,我們直接將各業務線小程序工程代碼拷貝到各自業務線目錄下,最終的目錄結構如下:
.├── common# 通用模塊/公共業務├── home# 首頁├── flight# 機票├── train# 火車票├── bus# 汽車票├── hotel# 酒店└── ticket# 門票
common 模塊即工程的復用部分,具體業務代碼都在各自目錄下。總體架構如下:
公共組件、API
微信小程序其實并沒有提供組件化開發的方式。只是提供了template的方式,所以我們只提供了為數不多的頁面及組件,例如:城市定位、日歷組件等。但是收益卻是非常明顯的,像這些頁面大小都在 20KB~30KB,如果每個業務自己整一套可能將徒增上百 KB 代碼。
公共 API 我們提供了統一的Watcher監控、Requester請求、Loading加載轉態、Navigation導航等。這些公共邏輯的抽取,為整個項目整合節省了巨大的空間,使 size 達標看起來不那么難了。
工程復用上節省了很大一部分空間。但是空間是有限的,業務需求是無限的。而且,size 的大小會影響用戶加載的速度,包括下載最新版本代碼的速度和小程序初始化的速度,所以還需要進一步進行代碼壓縮。
打包壓縮工具
微信開發者工具
我們知道微信小程序開發者工具本身提供了『代碼壓縮上傳』功能。
但是個人覺得它是個『假的壓縮選項』。因為在閱讀開發者工具的源碼邏輯之后,發現它的壓縮,只是將 JavaScript 用uglify進行混淆壓縮。而對 WXML、WXSS 沒有進行任何壓縮處理。同時,對資源路徑中的無用文件也沒有做處理。
小程序的構建
在小程序開發者工具的 Sources 面板,查看 JavaScript 腳本,會發現:項目中所有的 JavaScript 都會被同步加載,不管是否被require。
每個腳本都會被套上如下代碼:
define("some.js",function(require,module){// 原本的代碼});
這種加載方式類似 AMD,但是跟標準的 AMD 又有些不同,缺少了依賴部分的聲明。
而對于 WXSS 和 WXML 文件,則被開發者工具自動轉換為 JavaScript 后加載,其中:
WXSS:主要處理的是 import 邏輯,然后生成的 CSS,通過腳本的形式插入頁面使用。
WXML:類似于 React Naitive 的 JSX,被編譯成createElement類似的形式。
一些技巧
在 MacOS 系統中,右鍵開發者工具『Show Contents』(顯示包內容),就能在Resources/app.nw/下找到相應的源碼,完成路徑如下:/Applications/wechatwebdevtools.app/Contents/Resources/app.nw/。
源碼都是壓縮過后的 JavaScript 腳本,可以使用js-beautity進行格式化,以便于閱讀。
// 在源碼目錄的 app 目錄下執行find . -type f -name'*.js'-exec js-beautify -r-s2 -p-f'{}'\;
在資源目錄下: app/dist/app.js 的第 37 行window.addEventListener("resize", function() {})之前,加入nw.Window.get().showDevTools();。之后每次打開微信開發者工具時,會自動啟動針對『開發者工具』的開發者工具,并可以通過它調試微信的開發者工具。
在打印日志時,不要用console.log,請使用global.contentWindow.console.log。這樣,才能輸出到上面所說的開發者工具的開發者工具的控制臺里。(NW.js 的 Node JS Context 和 Webkit JS Context 是分開的, JavaScript 腳本運行在 Node 的 JS Context 中,因此,打印其實打印在 Node 的輸出中,并不在 Webkit 的開發者工具的控制臺中。global.contentWindow獲取的是 Webkit 的 JS Context 里的 Window)
使用這兩點技巧,讀者們可以優雅地去閱讀微信開發者工具的源碼了。
在閱讀源碼,知道微信小程序的內部加載構建方式之后,我們可以針對性的對 JavaScript、WXML、WXSS 等代碼文件進行打包壓縮。
壓縮 JavaScript
由于微信只是將js進行了混淆壓縮,并沒有打包合并成一個文件。所以我們提供了打包壓縮工具將js文件合并壓縮成一個bundle.js文件。合并成一個文件有以下好處:
require的長路徑沒了,腳本壓縮效率變高
代碼合并在一起,混淆性越大
文件合并,減少了 IO 次數,提升了加載效率
上文說了 JavaScript 都會被同步加載,所以不用擔心打包成一個文件后會延長小程序加載時間。
那么打包壓縮工具具體做了什么工作呢?
小程序有一個統一的入口是app.js,而每個頁面都有自己的入口page.js
利用 AST,將page.js里頁面注冊Page(pageOpt)代碼改成global.YPage(pageName, pageIndex)(pageOpt)
pageName頁面路由
pageIndex是打包工具根據頁面路由內部自動維護的
// global.YPage 函數global.YPage = (pageName, index)=> {return(pageOpt) => {// 其他處理邏輯global['p'+ index] = ()=> {? ? ? ? ? ? Page(pageOpt);? ? ? ? }? ? }}
這樣page.js里實際是這樣的代碼
global['p'+ index] = ()=> {? ? ? Page(pageOpt);}
這樣并不會執行Page(pageOpt),頁面也沒注冊啊。這就是要達到的目的,繼續往后看。
將所有這些入口require到一個統一的入口文件中,然后用 webpack 打包壓縮輸出到bundle.js。
require('app.js')require('page1.js')require('page2.js')...
現在page.js里的代碼都打包到單一文件bundle.js里了。將page.js內容替換成global['p' + index](),這樣第三步中的Page(pageOpt)不就可以執行注冊頁面了。
最后一步,替換app.js內容為require('./bundle.js')即大功告成。
壓縮 WXML、WXSS、JSON
WXML
//gm去除注釋
/\"\n\s*/g去除換行
WXSS
直接用uglifycss
JSON
直接JSON.stringify(JSON.parse(...))
這里,有些讀者會可能提出兩個疑問:
空白字符、換行能有多少,減不了多少吧?
開發者工具為什么不做對這些文件的壓縮?
關于第一個問題,一個約 1000KB 的代碼,空白字符和換行大概有 10KB。在有上限的情況之下,10 KB 也是要珍惜的。
關于第二個問題,個人認為微信開發者工具的開發者覺得沒有必要去做。因為,WXML 和 WXSS 都會被轉義成 JavaScript 腳本,在此過程中,不管 WXML 和 WXSS 是否被壓縮,它們的轉化結果是相同的。因此,壓縮與否,對于最終產物是沒有影響的(最終產物指在服務器二次打包后的結果,也是用戶真正使用的)。但是,Size 是以本地打包上傳的內容進行計算的,不進行此步壓縮,會使微信服務端判定的 Size 增大。
刪除無用文件
工程龐大了,肯定會存在很多無用的空目錄,空文件,沒有被import的js、wxml、wxss文件。
刪除這些無用文件不僅可以減小 size,還可以提升小程序加載時間。上文說了這些文件都會被轉成 JavaScript 進行加載的,是會占用加載時間的。
刪除無用 JavaScript
由于通過 webpack 打包,除了page.js、bundle .js文件,其他js文件都可以刪掉。
刪除無用 WXML
打包壓縮工具在打包時,已經記錄了所有頁面的路由。遍歷分析所有路由下的wxml文件,通過xmldom解析代碼記錄其他被import的wxml文件。最后遍歷所有wxml文件,刪除不在import列表里的無用wxml文件。
刪除無用 WXSS
類似處理 WXML。遍歷分析所有路由下的wxss文件,通過正則/@import\s*["']([^"']+)["']/g分析代碼記錄其他被import的wxss文件。最后遍歷所有wxss文件,刪除不在import列表里的無用wxss文件。
代碼級優化
除了利用工具進行壓縮,在編寫代碼時,也可以通過一些方法來減小體積,在這里簡單列幾點:
使用 ES6 時,盡量不使用依賴 Runtime/Polyfill 的語法,例如import和class。
圖標使用 Iconfont。
等等…
最終效果
經過打包工具的極限壓縮處理,還有代碼設計上的可復用性,目前我們七個業務線的微信小程序整合后代碼編譯包大小維持在 1300KB 左右。
還剩余 730KB 的可用空間。
結語
希望此次在微信小程序整合上做的,工程化抽取公共邏輯、規范業務代碼,通過工具進行針對性的代碼打包壓縮的實踐能給大家帶來一定的幫助