FlutterWeb 性能優化探索與實踐

美團外賣商家端基於 FlutterWeb 的技術探索已久,目前在多個業務中落地了 App、PC、H5 的多端複用,有效提升了產研的整體效率。在這過程中,性能問題是我們面臨的最大挑戰,本文結合實際業務場景進行思考,介紹美團外賣商家端在 FlutterWeb 性能優化上所進行的探索和實踐,希望對大家能有所幫助或啓發。

一、背景

1.1 關於 FlutterWeb

時間回撥到 2018 年,Google 首次公開 FlutterWeb Beta 版,表露出要實現一份代碼、多端運行的願景。經過無數工程師兩年多的努力,在今年年初(2021 年 3 月份),Flutter 2.0 正式對外發布,它將 FlutterWeb 功能併入了 Stable Channel,意味着 Google 更加堅定了多端複用的決心。

圖 1 FlutterWeb 歷史

當然 Google 的 “野心” 不是沒有底氣的,主要體現在它強大的跨端能力上,我們看一下 Flutter 的跨端能力在 Web 側是如何體現的:

圖 2 Flutter 跨端能力

上圖分別是 FlutterNative 和 FlutterWeb 的架構圖。通過對比可以看出,應用層 Framework 是公用的,意味着在 FlutterWeb 中我們也可以直接使用 Widgets、Gestures 等組件來實現邏輯跨端。而關於渲染跨端,FlutterWeb 提供了兩種模式來對齊 Engine 層的渲染能力:Canvaskit Render 和 HTML Render,下方表格對兩者的區別進行了對比:

圖 3 模式對比

Canvaskit Render 模式:底層基於 Skia 的 WebAssembly 版本,而上層使用 WebGL 進行渲染,因此能較好地保證一致性和滾動性能,但糟糕的兼容性(WebAssembly 從 Chrome 57 版本纔開始支持)是我們需要面對的問題。此外 Skia 的 WebAssembly 文件大小達到了 2.5M,且 Skia 自繪引擎需要字體庫支持,這意味着需要依賴超大的中文字體文件,對頁面加載性能影響較大,因此目前並不推薦在 Web 中直接使用 Canvaskit Render(官方也建議將 Canvaskit Render 模式用於桌面應用)。

HTML Render 模式:利用 HTML + Canvas 對齊了 Engine 層的渲染能力,因此兼容性表現優秀。另外,MTFlutterWeb 對滾動性能已有過探索和實踐,目前能夠應對大部分業務場景。而關於加載性能,此模式下的初始包爲 1.2M,是 Canvaskit Render 模式產物體積的 1/2,且我們可對編譯流程進行干預,控制輸出產物,因此優化空間較大。

基於以上原因,美團外賣技術團隊選擇在 HTML Render 模式下對 FlutterWeb 頁面的性能進行優化探索。

1.2 業務現狀

美團外賣商家端以 App、PC 等多元化的形態爲商家提供了訂單管理、商品維護、顧客評價、外賣課堂等一系列服務,且 App、PC 雙端業務功能基本對齊。此外,我們還在 PC 上特供了針對連鎖商家的多店管理功能。同時,爲滿足平臺運營訴求,部分業務具有外投 H5 場景,例如美團外賣商家課堂,它是一個以文章、視頻等形式幫助商家學習外賣運營知識、瞭解行業發展和跟進經營策略的內容平臺,具有較強的傳播屬性,因此我們提供了站外分享的能力。

圖 4 業務形態

爲了實現多端(App、PC、H5)複用,提升研發效率,我們於 2021 年年初開始着手 MTFlutterWeb 研發體系的建設。目前,我們基於 MTFlutterWeb 完成提效的業務超過了 9 個,在 App 中,能夠基於 FlutterNative 提供高性能的服務;在 PC 端和 Mobile 瀏覽器中,利用 FlutterWeb 做到了低成本適配,提升了產研的整體效率。

然而,加載性能問題是 MTFlutterWeb 應用推廣的最大障礙。這裏依然以美團外賣商家課堂業務爲例,在項目之初頁面完全加載時間 TP90 線達到了 6s 左右,距離我們的指標基線值(頁面完全加載時間 TP90 線不高於 3s,基線值主要依據美團外賣商家端的業務場景、用戶畫像等來確定)有些差距,用戶訪問體驗有很大的提升空間,因此 FlutterWeb 頁面加載性能優化,是我們亟需解決的問題。

二、挑戰

不過,想要突破 FlutterWeb 頁面加載的性能瓶頸,我們面臨的挑戰也是巨大的。這主要體現在 FlutterWeb 缺失靜態資源的優化策略,以及複雜的架構設計和編譯流程。下圖展示了 Flutter 業務代碼被轉換成 Web 平臺產物的流程,我們來具體進行分析:

圖 5 FlutterWeb 編譯流程

  1. Framework、Flutter_Web_SDK(Flutter_Web_SDK 基於 HTML、Canvas,承載 HTML Render 模式的具體實現)等底層 SDK 是可被業務代碼直接引入的,幫助我們快速開發出跨端應用;

  2. flutter_tools 是各平臺(Android、iOS、Web)的編譯入口,它接收 flutter build web 命令和參數並開始編譯流程,同時等待處理結果回調,在回調中我們可對編譯產物進行二次加工;

  3. frontend_server 負責將 Dart 轉換爲 AST,生成 kernel 中間產物 app.dill 文件(實際上各平臺的編譯過程都會生成這樣的中間產物),並交由各平臺 Compiler 進行轉譯;

  4. Dart2JS Compiler 是 Dart-SDK 中具體負責轉譯 JS 的模塊,它將上述中間產物 app.dill 進行讀取和解析,並注入 Math、List、Map 等 JS 工具方法,最終生產出 Web 平臺所能執行的 JS 文件。

  5. 編譯產物主要爲 main.dart.js、index.html、images 等靜態資源,FlutterWeb 對這些靜態資源缺少常規 Web 項目中的優化手段,例如:文件 Hash 化、文件分片、CDN 支持等。

可以看出,要完成對 FlutterWeb 編譯產物的優化,就需要干預 FlutterWeb 的衆多編譯模塊。而爲了提升整體的編譯效率,大部分模塊都被提前編譯成了 snapshot 文件( 一種 Dart 的編譯產物,可被 Dart VM 所運行,用於提升執行效率),例如:flutter_tools.snapshot、frontend_server.snapshot、dart2js.snapshot 等,這又加大了對 FlutterWeb 編譯流程進行干預的難度。

三、整體設計

如前文所述,爲了實現邏輯、渲染跨平臺,Flutter 的架構設計及編譯流程都具有一定的複雜性。但由於各平臺(Android、iOS、Web)的具體實現是解耦的,因此我們的思路是定位各模塊(Dart-SDK、Framework、Flutter_Web_SDK、flutter_tools)的 Web 平臺實現並尋求優化,整體設計圖如下所示:

圖 6 整體設計

下面,我們分別對各項優化進行詳細的說明。

四、設計與實踐

4.1 精簡 SDK

4.1.1 包體積分析

工欲善其事,必先利其器,在開始做體積裁剪之前,我們需要一套類似於 webpack-bundle-analyzer 的包體積分析工具,便於直觀地比較各個模塊的體積佔比,爲優化性能提供幫助。

Dart2JS 官方提供了 --dump-info 命令選項來分析 JS 產物,但其表現差強人意,它並不能很好地分析各個模塊的體積佔比。這裏更推薦使用 source-map-explorer ,它的原理是通過 sourcemap 文件進行反解,能清晰地反映出每個模塊的佔用大小,爲 SDK 的精簡提供了指引。下圖展示了 FlutterWeb JS 產物的反解信息(截圖僅包含 Framework 和 Flutter_Web_SDK):

圖 7 反解信息

4.1.2 SDK 裁剪

FlutterWeb 依賴的 SDK 主要包括 Dart-SDK、Framework 和 Flutter_Web_SDK,這些 SDK 對包體積的影響是巨大的,幾乎貢獻了初始化包的所有大小。雖然在 Release 模式下的編譯流程中,Dart Compiler 會利用 Tree-Shaking 來剔除那些引入但未使用的 packages、classes、functions 等,很大程度上減少了包體積。但這些 SDK 中仍然存在一些能被進一步優化的代碼。

以 Flutter Framework 爲例,由於它是全平臺公用的模塊,因此不可避免地存在各平臺的兼容邏輯(通常以 if-else、switch 等條件判斷形式出現),而這部分代碼是不能被 Tree-Shaking 剔除的,我們觀察如下的代碼:

// FileName: flutter/lib/src/rendering/editable.dart
void _handleKeyEvent(RawKeyEvent keyEvent) {
  if (kIsWeb) {
    // On web platform, we should ignore the key.
    return;
  }
  // Other codes ...
}

上述代碼選自 Framework 中的 RenderEditable 類,當 kIsWeb 變量爲真,表示當前應用運行在 Web 平臺。受限於 Tree-Shaking 的機制原理,上述代碼中,其它平臺的兼容邏輯即註釋 Other codes 的部分是無法被剔除的,但這部分代碼,對 Web 平臺來說卻是 Dead Code(永遠不可能被執行到的代碼),是可以被進一步優化的。

圖 8 部分功能構成

上圖展示了 SDK 的一部分功能構成,從圖中可以看出,FlutterWeb 依賴的這些 SDK 中包含了一些使用頻率較低的功能,例如:藍牙、USB、WebRTC、陀螺儀等功能的支持。爲此,我們提供了對這些長尾功能的定製能力(這些功能默認不開啓,但業務可配置),將未被啓用長尾的功能進行裁剪。

通過上述分析可得,我們的思路就是對 Dead Code 進行二次剔除,以及對這些長尾功能做裁剪。基於這樣的思路,我們深入 Dart-SDK、Framework 和 Flutter_Web_SDK 各個擊破,最終將 JS Bundle 產物體積由 1.2M 精簡至 0.7M,爲 FlutterWeb 頁面性能優化打下了堅實的基礎。

圖 9 精簡成果

4.1.3 SDK 集成 CI/CD

爲了提升構建效率,我們將 FlutterWeb 依賴的環境定製爲 Docker 鏡像,集成入 CI/CD(持續集成與部署)系統。SDK 裁剪後,我們需要更新 Docker 鏡像,整個過程耗時較長且不夠靈活。因此,我們將 Dart-SDK、Framework、Flutter_Web_SDK 按版本打包傳至雲端,在編譯開始前讀取 CI/CD 環境變量:sdk_version(SDK 版本號),遠程拉取相應版本的 SDK 包,並替換當前 Docker 環境中的對應模塊,基於以此方案實現 SDK 的靈活發佈,具體流程圖如下圖所示:

圖 10 集成 CI/CD

4.2 JS 分片

FlutterWeb 編譯之後默認會生成 main.dart.js 文件,它囊括了 SDK 代碼以及業務邏輯,這樣會引起以下問題:

  1. 功能無法及時更新:爲了實現瀏覽器的緩存優化,我們的項目開啓了對靜態資源的強緩存,若 main.dart.js 產物不支持 Hash 命名,可能導致程序代碼不能被及時更新;

  2. 無法使用 CDN:FlutterWeb 默認僅支持相對域名的資源加載方式,無法使用當前域名以外的 CDN 域名,導致無法享受 CDN 帶來的優勢;

  3. 首屏渲染性能不佳:雖然我們進行了 SDK 瘦身,但 main.dart.js 文件依然維持在 0.7M 以上,單一文件加載、解析時間過長,勢必會影響首屏的渲染時間。

針對文件 Hash 化和 CDN 加載的支持,我們在 flutter_tools 編譯流程中對靜態資源進行二次處理:遍歷靜態資源產物,增加文件 Hash (文件內容 MD5 值),並更新資源的引用;同時通過定製 Dart-SDK,修改了 main.dart.js、字體等靜態資源的加載邏輯,使其支持 CDN 資源加載。

更詳細的方案設計請參考《Flutter Web 在美團外賣的實踐》一文。下面我們重點介紹 main.dart.js 分片相關的一些優化策略。

4.2.1 Lazy Loading

Flutter 官方提供 deferred as 關鍵字來實現 Widget 的懶加載,而 dart2js 在編譯過程中可以將懶加載的 Widget 進行按需打包,這樣的拆包機制叫做 Lazy Loading。藉助 Lazy Loading,我們可以在路由表中使用 deferred 引入各個路由(頁面),以此來達到業務代碼拆離的目的,具體使用方法和效果如下所示:

// 使用方式
import 'pages/index/index.dart' deferred as IndexPageDefer;
{
  '/index'(context) => FutureBuilder(
    future: IndexPageDefer.loadLibrary(),
    builder: (context, snapshot) => IndexPageDefer.Demo(),
  )
  ... ...
}

圖 11 效果演示

使用 Lazy Loading 後,業務頁面的代碼會被拆分到了多個 PartJS(對應圖中 xxx.part.js 文件) 中。這樣看似解決了業務代碼與 SDK 耦合的問題,但在實際操作過程中,我們發現每次業務代碼的變動,仍然會導致編譯後的 main.dart.js 隨之發生變化(文件 Hash 值變化)。經過定位與跟蹤,我們發現這個變化的部分是 PartJS 的加載邏輯和映射關係,我們稱之爲 Runtime Manifest。因此,需要設計一套方案對 Runtime Manifest 進行抽離,來保證業務代碼的修改對 main.dart.js 的影響達到最低。

4.2.2 Runtime Manifest 抽離

通過對業務代碼的抽離,此時 main.dart.js 文件的構成主要包含 SDK 和  Runtime Manifest:

圖 12 main.dart.js 構成

那如何能將 Runtime Manifest 進行抽離呢?對比常規 Web 項目,我們的處理方式是把 SDK、Utils、三方包等基礎依賴,利用 Webpack、Rollup 等打包工具進行抽離並賦予一個穩定的 Hash 值。同時,將 Runtime Manifest (分片文件的加載邏輯和映射關係)注入到 HTML 文件中,這樣保證了業務代碼的變動不會影響到公共包。藉助常規 Web 項目的編譯思路,我們深入分析了 FlutterWeb 中 Runtime Manifest 的生成邏輯和 PartJS 的加載邏輯,定製出如下的解決方案:

圖 13 Runtime Manifest 抽離

在上圖中,Runtime Manifest 的生成邏輯位於 Dart2JS Compiler 模塊,在該生成邏輯中,我們對 Runtime Manifest 代碼塊進行了標記,之後在 flutter_tools 中將標記的 Runtime Manifest 代碼塊抽離並寫入 HTML 文件中(以 JS 常量形式存在)。而在 PartJS 的加載流程中,我們將 manifest 信息的讀取方式改爲了 JS 常量的獲取。按照這樣的拆分方式,業務代碼的變更只會改變 Runtime Manifest 信息 ,而不會影響到 main.dart.js 公共包。

4.2.3 main.dart.js 切片

經過以上引入 Lazy Loading、Runtime Manifest 抽離,main.dart.js 文件的體積穩定在 0.7M 左右,瀏覽器對大體積單文件的加載,會有很沉重的網絡負擔,所以我們設計了切片方案,充分地利用瀏覽器對多文件並行加載的特性,提升文件的加載效率。

具體實現方案爲:將 main.dart.js 在 flutter_tools 編譯過程拆分成多份純文本文件,前端通過 XHR 的方式並行加載並按順序拼接成 JavaScript 代碼置於 標籤中,從而實現切片文件的並行加載。

圖 14 並行加載

4.3 預加載方案

如上一節所述,雖然我們做了很多工作來穩定 main.dart.js 的內容,但在 Flutter Tree-Shaking 的運行機制下,各個項目引用不同的 Framework Widget,就會導致每個項目生成的 main.dart.js 內容不一致。隨着接入 FlutterWeb 的項目越來越多,每個業務的頁面互訪概率也越來越高,我們的期望是當訪問 A 業務時,可以預先緩存 B 業務引用的 main.dart.js,這樣當用戶真正進入 B 業務時就可以節省加載資源的時間,下面爲詳細的技術方案。

4.3.1 技術方案

我們把整體的技術方案分爲編譯、監聽、運行三個階段。

  1. 編譯階段,在發佈流水線上根據前期定製的匹配規則,篩選出符合條件的資源文件路徑,生成雲端 JSON 並上傳;

  2. 監聽階段,在 DOMContentLoaded 之後,對網絡資源、事件、DOM 變動進行監聽,並對監聽結果根據特定規則進行分析加權,得到一個首屏加載完成的狀態標識;

  3. 運行階段,在首屏加載完成之後對配置平臺下發的雲端 JSON 文件進行解析,對符合配置規則的資源進行 HTTP XHR 預加載,從而實現文件的預緩存功能。

下圖爲預緩存的整體方案設計:

圖 15 預緩存方案設計

編譯階段

編譯階段會擴展現有的發佈流水線,在 flutter build 之後增加 prefetch build 作業,這樣 build 之後就可以對產物目錄進行遍歷和篩選,得到我們所需資源進而生成雲端 JSON,爲運行階段提供數據基礎。下面的流程圖爲編譯階段的詳細方案設計:

圖 16 預緩存編譯階段

編譯階段分爲三部分:

  1. 第一部分:根據不同的發佈環境,初始化線上 / 線下的配置平臺,爲配置文件的讀寫做好準備;

  2. 第二部分:下載並解析配置平臺下發的資源組 JSON,篩選出符合配置規則的資源路徑,更新 JSON 文件併發布到配置平臺;

  3. 第三部分:通過發佈流水線提供的 API,把 PROJECT_ID、發佈環境注入 HTML 文件中,爲運行階段提供全局變量以便讀取。

通過對流水線編譯期的整合,我們可以生成新的雲端 JSON 並上傳到雲端,爲運行階段的下發提供數據基礎。

監聽階段

我們知道,瀏覽器對文件請求的併發數量是有限制的,爲了保證瀏覽器對當前頁面的渲染處於高優先級,同時還能完成預緩存的功能,我們設計了一套對緩存文件的加載策略,在不影響當前頁面加載的情況下,實現對緩存文件的加載操作。以下爲詳細的技術方案:

圖 17 預緩存監聽階段

在頁面 DOMContentLoaded 之後,我們會監聽三部分的的變化。

  1. 第一部分是監聽 DOM 的變化。這部分主要是在頁面發生 Ajax 請求之後,隨着 MV 模式的變動,DOM 也會隨之發生變化。我們使用瀏覽器提供的 MutationObserver API 對 DOM 變化進行收集,並篩選有效節點進行深度優先遍歷,計算每個 DOM 的遞歸權重值,低於閾值我們就認爲首屏已加載完成。

  2. 第二部分是監聽資源的變化。我們利用瀏覽提供的 PerformanceObserver API,篩選出 img/script 類型的資源,在 3 秒內收集的資源沒有增加時,我們認爲首屏已加載完成。

  3. 第三部分是監聽 Event 事件。當用戶發生 click、wheel、touchmove 等交互行爲時,我們就認爲當前頁面處於一個可交互的狀態,即首屏加載已完成,這樣會在後續進行資源的預緩存。

通過上述步驟,我們就可以得到一個首屏渲染完成的時機,之後就可以實現預緩存功能了。以下爲預緩存功能的實現。

運行階段

預緩存的整體流程爲:下載編譯階段生成的雲端 JSON,解析出需要進行預緩存資源的 CDN 路徑,最後通過 HTTP XHR 進行緩存資源進行請求,利用瀏覽器本身的緩存策略,把其他業務的資源文件寫入。當用戶訪問已命中緩存的頁面時,資源已被提前加載,這樣可以有效地減少首屏的加載時間。下圖爲運行階段的詳細方案設計:

圖 18 預緩存運行階段

在監聽階段,我們可以獲取到頁面的首屏渲染完成的時機,會獲取到雲端 JSON,首先判斷該項目的緩存是否爲啓用狀態。當該項目可用時,會根據全局變量 PROJECT_ID 進行資源數組的匹配,再以 HTTP XHR 方式進行預訪問,把緩存文件寫入瀏覽器緩存池中。至此,資源預緩存已執行完畢。

4.3.2 效果展示與數據對比

當有頁面間互訪問命中預緩存時,瀏覽器會以 200(Disk Cache)的方式返回數據,這樣就節省了大量資源加載的時間,下圖爲命中緩存後資源加載情況:

圖 19 預緩存效果展示

目前,美團外賣商家端業務已有 10+ 個頁面接入了預緩存功能,資源加載 90 線平均值由 400ms 下降到 350ms,降低了 12.5%;50 線平均值由 114ms 下降到 100ms,降低了 12%。隨着項目接入接入越來越多,預緩存的效果也會越發的明顯。

圖 20 預緩存數據展示

4.4 分平臺打包

如前文所述,美團外賣商家業務大部分都是雙端對齊的。爲了實現提效的最大化,我們對 FlutterWeb 的多平臺適配能力進行加強,實現了 FlutterWeb 在 PC 側的複用。

在 PC 適配過程中,我們不可避免地需要書寫雙端的兼容代碼,如:爲了實現在列表頁面中對卡片組件的複用。爲此我們開發了一個適配工具 ResponsiveSystem,分別傳入 PC 和 App 的各端實現,內部會區分平臺完成適配:

// ResponsiveSystem 使用舉例
Container(
  child: ResponsiveSystem(
    app: AppWidget(),
    pc: PCWidget(),
  ),
)

上述代碼能較方便的實現 PC 和 App 適配,但 AppWidget 或 PCWidget 在編譯過程中都將無法被 Tree-Shaking 去除,因此會影響包體積大小。對此,我們將編譯流程進行優化,設計分平臺打包方案:

圖 21 分平臺打包

  1. 修改 flutter-cli,使其支持 --responsiveSystem 命令行參數;

  2. 我們在 flutter_tools 中的 AST 分析階段增加了額外的處理:ResponsiveSystem 關鍵字的匹配,同時結合編譯平臺(PC 或 Mobile)來進行 AST 節點的改寫;

  3. 去除無用 AST 節點後,生成各個平臺的代碼快照(每份快照僅包含單獨平臺代碼);

  4. 根據代碼快照編譯生成 PC 和 App 兩套 JS 產物,並進行資源隔離。而對於 images、fonts 等公用資源,我們將其打入 common 目錄。

通過這樣的方式,我們去除了各自平臺的無用代碼,避免了 PC 適配過程中引起的包體積問題。依然以美團外賣商家課堂業務(6 個頁面)爲例,接入分平臺打包後,單平臺代碼體積減小 100KB 左右。

圖 22 效果展示

4.5 圖標字體精簡

當訪問 FlutterWeb 頁面時,即使在業務代碼中並未使用 Icon 圖標,也會加載一個 920KB 的圖標字體文件:MaterialIcons-Regular.woff。通過探究,我們發現是 Flutter Framework 中一些系統 UI 組件(如:CalendarDatePicker、PaginatedDataTable、PopupMenuButton 等)使用到了 Icon 圖標導致,且 Flutter 爲了便於開發者使用,提供了全量的 Icon 圖標字體文件。

Flutter 官方提供的 --tree-shake-icons 命令選項是將業務使用到的 Icon 與 Flutter 內部維護的一個縮小版字體文件(大約 690KB)進行合併,能一定程度上減小字體文件大小。而我們需要的是隻打包業務使用的 Icon,所以我們對官方 tree-shake-icons 進行了優化,設計了 Icon 的按需打包方案:

圖 23 圖標字體精簡

  1. 掃描全部業務代碼以及依賴的 Plugins、Packages、Flutter Framework,分析出所有用到的 Icon;

  2. 把掃描到的所有 Icon 與  material/icons.dart(該文件包含 Flutter Icon 的 unicode 編碼集合)進行對比,得到精簡後的圖標編碼列表:iconStrList;

  3. 使用 FontTools 工具把 iconStrList 生成字體文件 .woff,此時的字體文件僅包含真正使用到的 Icon。

通過以上的方案,我們解決了字體文件過大帶來的包體積問題,以美團外賣課堂業務(業務代碼中使用了 5 個 Icon)爲例,字體文件從 920KB 精簡爲 11.6kB。

圖 24 效果展示

五、總結與展望

綜上所述,我們基於 HTML Render 模式對 FlutterWeb 性能優化進行了探索和實踐,主要包括 SDK(Dart-SDK、Framework、Flutter_Web_SDK)的精簡,靜態資源產物優化(例如:JS 分片、文件 Hash、字體圖標文件精簡、分平臺打包等)和前端資源加載優化(預加載與按需請求)。最終使得 JS 產物由 1.2M 減少至 0.7M(非業務代碼),頁面完全加載時間 TP90 線由 6s 降到了 3s,這樣的結果已能滿足美團外賣商家端的大部分業務要求。而未來的規劃將聚焦於以下 3 個方向:

  1. 降低 Web 端適配成本:目前已有 9+ 個業務藉助 MTFlutterWeb 實現多端複用,但在 Web 側(尤其是 PC 側)的適配效率依然有優化空間,目標是將適配成本降低到 10% 以下(目前大約是 20% );

  2. 構建 FlutterWeb 容災體系:Flutter 動態化包有一定的加載失敗概率,而 FlutterWeb 作爲兜底方案,能提升整體業務的加載成功率。此外 FlutterWeb 可以提供 “免安裝更新” 的能力,降低 FlutterNative 老舊歷史版本的維護成本;

  3. 性能優化的持續推進:性能優化的階段性成果爲 MTFlutterWeb 的應用推廣鞏固了基礎,但依然是有進一步優化空間的,例如:目前我們僅將業務代碼和 Runtime Manifest 進行了拆離,而 Framework 及 三方包在一定程度上也影響到了瀏覽器緩存的命中率,將這部分代碼進行抽離,可進一步提升頁面加載性能。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/fE11vs2qnRIHAWYv1af-gA