京喜小程序首頁瘦身實踐

前言

在 web 開發場景,減小代碼體積雖然是性能優化的一個方向,還沒到錙銖必較的程度。可是在小程序場景,因爲代碼包上傳階段限制了主包 2M 和總包 16M(近期微信官方正在內測將總包上限調整至 20M )的尺寸,超過就會面臨沒法發版的風險,代碼包體積的優化就變得特別重要了。
京喜小程序首頁做爲微信購物的大入口,承載大量流量,功能複雜模塊衆多,又要與其餘核心業務和公共組件共享 2M 的主包空間,所以代碼包瘦身的工做在持續不斷進行,不然沒法知足業務的快速增加。本文將結合以往優化策略與最近一次的瘦身實踐,分享小程序代碼瘦身的經驗與思考。css

常見的瘦身方式

京喜首頁項目是一個優化良好的項目,對於常見的優化措施,已經有過很好的實踐,就讓咱們咱們先回顧一下有哪些常見的優化策略吧:git

  1. 代碼分包:將相對獨立的頁面和組件拆分到分包,能夠解決主包體積受限問題;
  2. 依賴分析:移除未引用的頁面、組件和其餘文件;
  3. 避免使用本地資源:除了兜底圖片,其餘都儘量使用 url 的方式,因爲 base64 圖本質上是將信息編碼成長字符串,也會佔用不少空間,不建議使用;
  4. 對全部類型的文件都進行壓縮並清理註釋,包括了:js、wxml、wxss、json;

此外,京喜首頁團隊還針對 Taro 開發場景進行了以下優化:github

  1. 分析出編譯後每一個文件的高頻重複代碼(如處理兼容性的 pollyfill 代碼),拆分生成公共文件,替換原引用以實現共用。

標準和工具

在開始正式介紹瘦身實踐以前,咱們先來明確一下代碼包體積的衡量標準和統計方式吧。web

小程序上傳代碼以代碼包尺寸爲準,所謂 2M 的限制,就是指該尺寸不能超過 2048KB。npm

從信息傳輸角度來講,Gzip 等壓縮工具能夠進行不少信息化編碼優化,所以一些內容重複是能夠容忍的,可是因爲咱們的目標是爲了解決小程序上傳限制,就只有對代碼包尺寸錙銖必較了。json

在開發者工具-詳情-基本信息-上次預覽或上次上傳,能夠查看到最近一次的代碼包體積,本文接下來所介紹的優化都是以縮小這個體積爲目的。小程序

查看代碼包尺寸

可是代碼上傳生成模板速度很慢,若是每次都要根據這裏的數據來統計體積變化,效率過低了。微信小程序

在未改動項目配置的狀況下,咱們就能夠間接以代碼目錄的文件體積大小做爲變化參照。怎麼方便的統計文件體積呢?這裏我用了tree-cli,利用它提供的參數,能夠輸出具有尺寸統計和排序功能的代碼文件清單:瀏覽器

npm install -g tree-cli
// 目標目錄
cd target-directory
// 輸出文件爲 size-analysis.md
tree -s --sort=size -o size-analysis.md

清單內容格式以下:sass

.
├── [      1000]  index.js
├── [       500]  index.wxss
├── [       500]  index.wxml
├── [       500]  index.json
├── [      4000]  components
│   ├── [      4000]  child
│   │   ├── [      1000]  index.js
│   │   ├── [      1000]  index.wxml
│   │   ├── [      1000]  index.wxss
└── └── └── [      1000]  index.json
  
6500 bytes used in 2 directories, 8 files

瘦身實踐

前面說到京喜首頁優化措施都作的很好了,下面即將分享的是一些不那麼常見的優化方式,優化空間有大有小,想要優化小程序代碼包,建議先儘可能完成前文提到的優化方案,這樣得到的收益最明顯,而後再來看接下來提到的這些方式吧~

1、字體和顏色的全局共用

小程序文檔內關於繼承樣式的說明爲:繼承樣式,如font,color, 會從組件外繼承到組件內。

分析項目現狀,咱們一般會把字體定義放在公共 css 文件內,隨着頁面或組件引入公共 css,font 也將被重複引入,能夠經過改造,把 font 的定義僅放在 app.wxss 內,取消組件和頁面的引入,能夠達到減小總體代碼包體積的目的。

關於這一項首頁項目體積減小1%,預估整個項目還有 20kb 左右的 font 定義可清理。

若是有全局的顏色定義,也能夠進行相似的優化。

2、樣式補全功能的使用

做爲 web 開發者,對 -webkit- 這種前綴必定不陌生,爲了適配不一樣瀏覽器內核,一般咱們會在編譯階段使用 autoprefixer 進行樣式的自動補全。

而小程序開發者工具也提供了樣式補全的能力:詳情-本地設置-能夠勾選「上傳代碼時樣式自動補全」

這個補全和咱們在編譯時作的有什麼不一樣嗎?

關鍵在於它實現的時機:若是是本地模板上傳前,那麼應該和咱們編譯的補全效果同樣;若是是在上傳模板後,也許能夠藉此減掉補全內容所佔的尺寸。

結合小程序代碼包傳遞過程和樣式補全時機,大概有如下3種狀況:

階段一補全:

image-20201119103740502

階段二:

image-20201119103807546

或者是階段三:

image-20201119103830070

爲了驗證猜測,來作一個實驗吧,比較「 項目編譯不補全樣式+開發者工具設置樣式補全」 vs「 項目編譯補全樣式+開發者工具不設置樣式補全」,模板體積統計以下:

開發者工具提供樣式補全

編譯提供樣式補全

可見前者比後者少了 58kb,這說明,開發者工具提供的樣式補全不是在階段一作的,否則模板體積應該和咱們本身作的編譯補全基本一致。

那麼,就能夠愉快的去掉編譯補全,使用小程序開發者工具提供的能力了。

不過這樣改動會出現一個小問題,開發者工具內的樣式是未經補全處理的,個別樣式會有點問題,測試就發現 mask-border-source 無效,而相應真機由於已添加樣式補全沒有問題。爲了避免出現預覽誤會,建議給這種還沒有支持的樣式手動寫上 -webkit- 前綴,保證開發和真機表現一致。

3、當心 Sass!

sass/less 等工具使得 css 的編寫變得更加流暢,函數和變量的引入也讓 css 有了一點工程化的意味。可是你有沒有觀察過 sass 的編譯實現呢?

// a.scss,做爲被引用方
.banner {         // 樣式定義
    color: red;
}
$COLOR = red     //  變量定義(函數定義相似)

// b.scss,做爲使用方
@import 'a.scss';
.banner_wrapper {
    background: white;
    color:$COLOR;
}

關注b.sass的編譯後:

// a.scss的引用消失了,內容被整合到文件內

.banner {              // a.scss內的樣式定義會被拷貝進來
    color: red;
}
.banner_wrapper {
    background: white;
    color:red;           //變量定義會被按值替換
}

這裏出現的問題是:咱們是否須要.banner被拷貝進來呢

爲了不多引入不須要的樣式定義,有如下幾個方向:

  1. 按功能拆分 a.scss 內的樣式定義,按需引入。
  2. 使用 @include 語法,將 banner 的定義變成一個變量,按需引入。

而在小程序場景,wxss 語法支持 @import,實現了極弱版的模塊化,使得咱們能夠再加一個角度解決上面的問題:

  1. 繞過 sass 編譯,使用小程序的 @import 語法,引入須要的樣式定義。(關於如何繞開 sass 編譯,能夠考慮使用註釋片斷,或者白名單篩選識別)

4、多端場景的冗餘代碼移除

京喜首頁項目使用 Taro 開發,須要適配 H5/微信小程序/QQ小程序等多端場景,利用 Taro 提供的環境變量能力,能夠在方法內部實現多端差別處理,好比下面這段:

init(){
  if(process.env.TARO_ENV === 'weapp'{
     // 微信小程序邏輯
     this.initWeapp()
  }else if(process.env.TARO_ENV === 'h5'){
     // H5頁面邏輯
     this.initH5()
  }
}
initWeapp(){...}
initH5(){...}

小程序端打包後代碼:

init(){
    this.initWeapp()
}
initWeapp(){...}
initH5(){...}

可是,環境變量方式沒辦法處理 initH5 這種方法定義,致使也被打包進來了。

所以,咱們須要更強大的差別打包:京喜首頁利用內部的 wxa-cli 工具提供的條件編譯能力,經過註釋段落標記,圈注出多端內容,實現了代碼片斷層面的差別打包,細節以下:

init(){
  if(process.env.TARO_ENV === 'weapp'{
     // 微信小程序邏輯
     this.initWeapp()
  }else if(process.env.TARO_ENV === 'h5'){
     // H5頁面邏輯
     this.initH5()
  }
}
initWeapp(){...}
/* wxa if:type=='h5' */  標記h5端代碼開始位置
initH5(){...}
/* /wxa  */              標記註釋結束位置

打包後代碼:

init(){          // weapp內
    this.initWeapp()
}
initWeapp(){...}

initH5 消失了,代碼更瘦了

5、整理 log

爲了調試方便,你的項目內有沒有打很長的 log,相似於這種:

console.log('==============xx接口異常============')

通過測試,首頁代碼文件內有 5KB 的內容是 log 語句,能夠試着優化一下:

  1. 及時移除開發調試用 log
  2. 信息類 log 約定長度更短的格式

6、良好的編碼策略

有沒有一樣的邏輯需求,能夠用更短更優雅的寫法來實現呢?

關於代碼分析是個很複雜的話題,暫時列一個結論相對明確的寫法吧

格式化數據時數據的存取和中間變量問題

function format(list){
 let result = []
 list.forEach(item => {
    const {
      a,
      b,
      c: f,
      d,
      e,
    } = item

    result.push({
      a,
      b,
      f,
      d,
      e,
    })
  })

  return result

能夠利用 lodash 的 pick 方法改寫成:

import { pick } from 'lodash/pick'

function format(list){
 return list.map(item=>({
     ...pick(item,'a','b','d','e'),
     f:item.c
 }))

7、樣式命名編譯優化

京喜首頁項目因爲 H5 端混搭老項目,爲了不類名衝突,採用了形如block-name__element--modifier的 bem 命名規則。在開發中進一步發現,一些相似 navbar-content__item 的常見命名偶爾撞車,爲了不衝突,類名就越寫越長,而小程序代碼包的尺寸影響也在悄悄增大。

爲了解決命名衝突的問題,將類名 hash 化是個好辦法,css-modules 就是個成熟的插件,能夠經過配置規則,對樣式名編譯出「文件名+內容相關」的獨特化 hash。

可是研究下它的實現,會發現對代碼尺寸的影響不容樂觀,看一個編譯後例子:

import style from './index.module.map.scss.js' //js文件,增長一句jsMap的引入

<view className={style.banner}></view>  // wxml文件,每處類名都比原類名增長了`style.`的引用 

.hash { xx }   // wxss文件, 類名被hash化,減小的具體尺寸爲:原類名-hash 

module.exports = { banner : hash }  // 新增了一個map文件,實現原名與hash名的映射,增長的具體尺寸爲:原類名+hash

計算總體內容變化:

  1. js 內新增引入 map 語句:增長一句代碼
  2. wxml 內:原爲 n 個類名,現爲 n 個「style.+原類名」,增量爲 n 個style.
  3. map 文件 與 wxss 文件合計:map 內有 n 個原類名與 hash 映射, wxss 現爲 n 個 hash , 減去原來的 n 個原類名 ,合計增量爲 2n 個 hash

可見引入 css-modules 會致使總體代碼尺寸增長。

會不會以爲這個新增的 map 文件的做用特別熟悉呢?

在咱們壓縮 js 文件時,會有一個 sourceMap 文件,它保留了原始命名和代碼位置,能夠方便定位和 debug。

css-modules 實現的 map 文件,在我看來做用和 sourceMap 的命名索引差很少,對於代碼邏輯來講,除了保持原類名的引用信息,它好像也沒什麼用了,在尺寸敏感場景,就能夠考慮去掉 map 文件,仍是上文的示例,若是能夠實現成這樣就行了:

// import style from './index.module.map.scss.js'   js文件取消map的引入

// wxml文件
<view className="hash"></view>   // 對style.banner進行求值並替換  

// wxss文件
.hash { xx }    // 這裏不變

module.exports = { banner : hash }   // 刪掉不要

網上遍尋沒有相關的處理,只能本身造輪子開搞了。

因爲當前主要目的是對小程序代碼瘦身,H5 端文件處理和小程序也有一些差別,因此暫時只對小程序場景造了插件,取名 weapp-css-modules ,github 地址在這裏:https://github.com/o2team/wea...

大概思路是:

  1. 完成小程序的 css-modules 實現
  2. 在此基礎上進行 map 移除的相關簡化邏輯
  3. 進一步的,考慮到小程序組件內默認樣式隔離的特性,對 hash 化的命名再次縮短,變成單字母編排。

若是是隻開發小程序端,能夠藉此實現小程序樣式命名相關的代碼瘦身,而對於 Taro 開發的多端場景,還能夠同時解決 h5 端的命名衝突問題。

仍是上面的例子,下面是 weapp-css-modules 編譯後效果:

// js文件
let style = {}    // 不引用map,加入對不規範引入style的兼容

// wxml文件
<view className="a"></view>   // 對style.banner進行求值並替換,加入單字母編排

// wxss文件
.a { xx }     // 由於小程序組件樣式隔離,因此能夠最短化類名

module.exports = { banner : hash }        // 刪掉不要

京喜首頁項目經過改造組件採用 css-modules 寫法,加上 weapp-css-modules 編譯,代碼相對尺寸減小了 10%,仍是頗有效果的,感興趣的同窗能夠試用一下。

總結

關於代碼瘦身,想提一下信息學中熵的概念:熵反映信息的無序程度,一段信息無序程度越低,它的熵值越低,可被壓縮的空間越大;無序程度越高,熵值越高,可被壓縮的空間越小。而數據壓縮或者是代碼瘦身的過程,就是經過優化信息存儲方式以逼近它真實的熵值。

從這個角度來講:

  • 「字體和顏色的全局共用」和「樣式補全功能的使用」是借用小程序提供的能力,信息量沒變;
  • 「當心 Sass」、「多端場景的冗餘代碼移除」是減小不用的信息;
  • 「整理 log」和「樣式命名編譯優化」是凝練有效信息;

看起來最很差歸類的是「良好的編碼策略」,它是在編碼階段對信息的梳理和整合,也算凝練有效信息吧。

以上就是京喜首頁項目此次代碼瘦身的主要方式了,除此以外的刪除不用文件、整合公共文件這些體力活,我就再也不囉嗦了。經過以上方式,京喜首頁代碼在本來優化良好的基礎上,實現了再次減重30%的目標,但願能給小程序開發者們帶來有價值的信息和思考。

參考資料

[1] CSS Modules: https://github.com/css-module...

[2] Tree-Cli: https://github.com/MrRaindrop...

[3] 小程序工程化探索: https://mp.weixin.qq.com/s/_N...

[4] 微信小程序 限制2M的瘦身技巧與方法詳解: https://blog.csdn.net/wlanye/...


歡迎關注凹凸實驗室博客:aotu.io

或者關注凹凸實驗室公衆號(AOTULabs),不定時推送文章。

相關文章
相關標籤/搜索