小程式分包的一些思考及Uiniapp 分包優化邏輯的驗證

前端小智發表於2022-02-22

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

分包

什麼是分包

分包指的是把一個完整的小程式專案,按照需求劃分為不同的子包,在構建時打包成不同的分包,使用者在使用時按需進行載入。

分包的好處

對小程式進行分包的好處主要有以下兩點:

  • 可以優化小程式首次啟動的下載時間
  • 在多團隊共同開發時可以更好的解耦協作

uniapp 分包配置

按官網,假設支援分包的 uni-app 目錄結構如下:

┌─pages               
│  ├─index
│  │  └─index.vue    
│  └─login
│     └─login.vue    
├─pagesA   
│  ├─static
│  └─list
│     └─list.vue 
├─pagesB    
│  ├─static
│  └─detail
│     └─detail.vue  
├─static             
├─main.js       
├─App.vue          
├─manifest.json  
└─pages.json            

則需要在 pages.json 中填寫:

{
    "pages": [{
        "path": "pages/index/index",
        "style": { ...}
    }, {
        "path": "pages/login/login",
        "style": { ...}
    }],
    "subPackages": [{
        "root": "pagesA",
        "name": "分包的別名"
        "pages": [{
            "path": "list/list",
            "style": { ...}
        }]
    }, {
        "root": "pagesB",
        "pages": [{
            "path": "detail/detail",
            "style": { ...}
        }]
    }],
}

動手實踐:https://github.com/qq44924588...

執行後,可以在開發者詳情裡檢視分包資訊:

這樣配置的目錄結構真的好?

按官網的事例,如果我們要分兩個包,則對在 pages 同級下建立兩個目錄 pagesApagesB,這樣劃分真的好嗎?假設我們有一個活動的業務模板,對應 的活動詳情地址是 /pages/activity/detailpages.json 配置如下:

    "pages": [
        {
            "path": "pages/index/index",
            "style": {
                "navigationBarTitleText": "首頁"
            }
        },
        {
            "path": "pages/activity/detail",
            "style": {
                "navigationBarTitleText": "活動詳情"
            }
        }
    ],

隨著業務的發展,該模組的功能也越來越多。哪天,我們要對該包進行分包的時候,如果按照前面的分包方式,我們如果在 pages 同級下建立一個子包的目錄,假設我們這裡叫 pagesA,然後把對應的活動模組的檔案都挪動到該目錄下,對應的 pages.json配置如下:

"subPackages": [{
  "root": "pagesA",
  "pages": [{
    "path": "activity/detail"
   }]
}]

此時,對應的 活動詳情地址是 /pagesA/activity/detail,這樣就會引發一個問題,此時活動詳情路徑已經變化了,所以分包要能正常工作,之前的路徑都要改過來,如果有其它小程式有跳到該詳情頁面也得改,顯然這種分包結構是很不靠譜的,為了一個分包得改多個檔案,甚至多個小程式。

那要怎麼解決這個問題了?顯然,只要路徑不變化,但能正確分包不就可以解決這個問題了。

在細想一下分包,無非就是在 subPackages 中指定一個分包名,分包頁面對應該分包名下的檔案。所以,我們可以指定原有 pages 下的模組作為一個子分包,這樣配置就可以解決分包後路徑不一致的問題,改寫後的結構如下:

  "subPackages": [
    {
      "root": "pages/activity",
      "pages": [
        {
          "path": "detail"
        }
      ]
    }
  ],

事例地址:https://github.com/qq44924588...

分包預載入

分包預載配置。配置 preloadRule 後,在進入小程式某個頁面時,由框架自動預下載可能需要的分包,提升進入後續分包頁面時的啟動速度。

假設,我們兩個分包 pagesApagesB ,當我們進入詳情頁面,想預先加 pagesA ,對應的配置如下:

{
    "pages": [ //pages陣列中第一項表示應用啟動頁,參考:https://uniapp.dcloud.io/collocation/pages
        {
            "path": "pages/index/index",
            "style": {
                "navigationBarTitleText": "首頁"
            }
        },
        {
            "path": "pages/detail/index",
            "style": {
                "navigationBarTitleText": "詳情"
            }
        }
    ],
    "subPackages": [{
        "root": "pagesA",
        "pages": [{
            "path": "detail/index"
        }]
    }, {
        "root": "pagesB",
        "pages": [{
            "path": "detail/index"
        }]
    }],
    "preloadRule": {
        "pages/detail/index": {
            "network": "wifi",
            "packages": ["pagesA", "pagesB"]
        }
    }
}

preloadRule 中,key 是頁面路徑,value 是進入此頁面的預下載配置, packages 是進入頁面後預下載分包的 。network 在指定網路下預下載,可選值為:all(不限網路)、wifi(僅wifi下預下載)。

事例地址:

https://github.com/qq44924588...

分包的載入規則

分包需要注意一點是:tabBar 頁面需要放在主包中,假設我們如兩個 tabBar,配置如下:

{
    "pages": [
        {
      "path": "pages/channel/index",
      "style": {
        "disableScroll": true
      }
    },
    {
      "path": "pages/member/index",
      "style": {
        "disableScroll": true
        }
      }
    ],
    "tabBar": {
    "color": "#BBBBBD",
    "selectedColor": "#1C1C1C",
    "borderStyle": "white",
    "backgroundColor": "#ffffff",
    "list": [
      {
        "pagePath": "pages/channel/index",
        "iconPath": "static/tabbar_icon_channel.png",
        "selectedIconPath": "static/tabbar_icon_channel_active.png",
        "text": "會員專屬"
      },
      {
        "pagePath": "pages/member/index",
        "iconPath": "static/tabbar_icon_member.png",
        "selectedIconPath": "static/tabbar_icon_member_active.png",
        "text": "掌通會員"
      }
    ]
  }
}

如果我們把 tabBar 頁面配置到 subPackages,則會出錯:

事例地址:https://github.com/qq44924588...

分包優化

在對應平臺的配置下新增"optimization":{"subPackages":true}開啟分包優化。

分包優化具體邏輯

  • 靜態檔案:分包下支援 static 等靜態資源拷貝,即分包目錄內放置的靜態資源不會被打包到主包中,也不可在主包中使用
  • js檔案:當某個 js 僅被一個分包引用時,該 js 會被打包到該分包內,否則仍打到主包(即被主包引用,或被超過 1 個分包引用)
  • 自定義元件:若某個自定義元件僅被一個分包引用時,且未放入到分包內,編譯時會輸出提示資訊

首先,我們來化驗一下第一條規則,首頁建立對應的配置:

{
    "pages": [
        {
            "path": "pages/index/index",
        }
    ],
    "subPackages": [{
        "root": "pagesA",
        "pages": [{
            "path": "detail/index"
        }]
    }],
}

這裡,我們有一個分包 pagesA 對應一個詳情頁面,詳情頁面使用一張圖片,該圖片放置當前包下 static 檔案下,內容如下:

  <view class="content">
    <image class="logo" src="../static/test.png" />
  </view>

然後,我們開始打包,然後點選開發者工具的 詳情,點選 原生程式碼-> 依賴分析,就可以檢視打包的詳細資訊,如下圖所示:

可以看到,我們點選 主包 下的 static 是沒有我們的在分包中用的 test.png 圖片,相反,它被打包自己的分包下面。即分包目錄內放置的靜態資源不會被打包到主包中,成立

接下來,在來驗證一下,如果在主包中使用分包的 test.png 圖片會怎麼樣?改寫一上我們主包的 index 檔案的內容:

<view class="content">
  <image class="logo" src="../../pagesA/static/test.png" />
</view>

執行後,控制檯會給出錯誤:

所以,分包下支援 static 等靜態資源拷貝,即分包目錄內放置的靜態資源不會被打包到主包中,也不可在主包中使用 成立。

事例地址:https://github.com/qq44924588...

然後,我們來驗證一下第二條規則:js檔案:當某個 js 僅被一個分包引用時,該 js 會被打包到該分包內,否則仍打到主包(即被主包引用,或被超過 1 個分包引用)

首先,我們在主包中的 src/utils中建立一個 common.ts,內容如下:

export const add = (a: number, b:number) => {
  return a + b
}

然後,我們在子包 pagesA 中匯入使用:

<script lang="ts">
import Vue from 'vue'
import { add } from '@/utils/common'
export default Vue.extend({
  created() {
    console.log(add(1, 2))
  }
})
</script>

同樣,打包,然後點選開發者工具的 詳情,點選 原生程式碼-> 依賴分析

從上圖可以看到,我們在 主包 中的的 vender.js 沒有找到 src/utils/common.ts 中的 add 方法,但在分包 pagesA 找到了。

那如果某個 js被多個分包所引用呢?

我們再建立一個分包 pagesB,與同樣的方式在子包 pagesB 中匯入common.ts

<script lang="ts">
import Vue from 'vue'
import { add } from '@/utils/common'
export default Vue.extend({
  created() {
    console.log(add(1, 2))
  }
})
</script>

同樣,打包,然後點選開發者工具的 詳情,點選 原生程式碼-> 依賴分析

從圖可以看到,我們在主包中的 vendor.js 可以找到我們使用的 add 方法,在分包中沒有找到對應的 vendor.js,所以當某個 js 僅被多個分包引用時,該 js 會被打包到主包

事例地址:https://github.com/qq44924588...

最後,我們來驗證一下第三條規則:自定義元件:若某個自定義元件僅被一個分包引用時,且未放入到分包內,編譯時會輸出提示資訊

首先,我們在主包中的 src/components 建立一個自定義元件 SayHello,內容如下:

<template>
    <view class="content">
    Hello World
    </view>
</template>

然後,以同樣的方式建個分包(步驟跟上面的例子一樣),在分包的中引用我們的元件 SayHello

<template>
  <view class="content">
    <SayHello />
  </view>
</template>

<script lang="ts">
import Vue from 'vue'
import SayHello from '@/components/SayHello.vue'

export default Vue.extend({
  components: {
    SayHello
  }
})
</script>

然後,編譯可以在控制檯看到提示的資訊:

那如果自定義元件被多個分包引用呢?提示應該會消失,為了嚴謹性,我們再建個分包 pagesB 以同樣姿勢引用元件,然後編譯,再檢視控制檯:

可以看到,提示資訊消失了。

至此,UniApp 官網提到分包優化具體邏輯我們都驗證過,Nice。

事例地址:https://github.com/qq44924588...


程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

交流

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588... 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章