当前位置: 首页 > news >正文

FlutterWeb实战:02-加载体验优化

背景

默认情况下,Flutter 打包 web 以后,首次打开页面需要加载大量的资源,这就需要做首屏加载优化。

渲染引擎

通过分析,canvaskit 和 skwasm 需要加载较大的引擎包,很难优化,目前选择 3.22 版本,故选择 HTML Render 引擎

Flutter Web 计划在 2025 开始弃用 HTML Render。如果是 2025 年的新版本,可以考虑使用 skwasm 引擎。

字体图标裁剪

体积裁剪,通过 bulid apk shaking icon,得到一个裁剪后的字体库,替换调 Flutter Web 打包的对应字体产物

先在 App 项目构建 apk:

flutter build apk --tree-shake-icons

找到 build/host/intermediates/assets/release/mergeReleaseAssets/flutter_assets/fonts/MaterialIcons-Regular.otf
将该文档复制到 web/fonts/ 文件夹

文件采样压缩前压缩后压缩率
MaterialIcons-Regular.otf1.5M2k1%

延迟加载

使用延迟加载拆分文件,当前页面不需要的使用的代码延迟加载

Dart 中提供了 defered 关键词,用于延迟加载组件。

参考下方实现一个 DeferredWidget 组件

import 'dart:async';
import 'package:ealing_widget/common/common_color.dart';
import 'package:flutter/material.dart';typedef LibraryLoader = Future<void> Function();
typedef DeferredWidgetBuilder = Widget Function();///延迟加载组件
class DeferredWidget extends StatefulWidget {DeferredWidget(this.libraryLoader, this.createWidget, {Key? key, Widget? placeholder}) : placeholder = placeholder ?? Container(color: CommonColors.color_widget_background), super(key: key);final LibraryLoader libraryLoader;final DeferredWidgetBuilder createWidget;final Widget placeholder;// 存储 libraryLoader 对应的 future 数据static final Map<LibraryLoader, Future<void>> _moduleLoaders = {};// 存储已经预加载过了的 libraryLoaderstatic final Set<LibraryLoader> _loadedModules = {};static Future<void>? preload(LibraryLoader loader) {if (!_moduleLoaders.containsKey(loader)) {_moduleLoaders[loader] = loader().then((dynamic _) {_loadedModules.add(loader);});}return _moduleLoaders[loader];}_DeferredWidgetState createState() => _DeferredWidgetState();
}class _DeferredWidgetState extends State<DeferredWidget> {Widget? _loadedChild;void initState() {if (DeferredWidget._loadedModules.contains(widget.libraryLoader)) {_onLibraryLoaded();} else {DeferredWidget.preload(widget.libraryLoader)?.then((dynamic _) => _onLibraryLoaded());}super.initState();}void _onLibraryLoaded() {setState(() {_loadedChild = widget.createWidget();});}Widget build(BuildContext context) {return _loadedChild ?? widget.placeholder;}
}

然后在 GoRouter 路由配置处, 以这种形式使用:


import '../screens/home/index.dart' deferred as home;final _router = GoRouter(routes: [GoRoute(path: '/',builder: (context, state) => ppDeferredWidget(libraryLoader: home.loadLibrary, builder: (() => home.HomeIndexScreen())),),],
);

经过以上配置, Flutter Web 打包后,将对 js 文件分割,只有在当前页面打开时,才会加载对应的 js 文件,这就实现了页面组件资源的延迟加载。

在这里插入图片描述

产物对比

经过加载对比可以看到,首屏加载时,原本 2M 左右的 main.dart.js 大小,减小到了 1M 左右,显著提升了首屏静态资源大小。
在这里插入图片描述

加载动画

增加过渡动画,在资源加载过程中使用一个加载动画,优化用户体验。

这里使用 flutter_native_splash 插件,在 app 启动时,显示一个加载动画,在 app 加载完成后,隐藏加载动画。

<body><picture id="splash"><img class="center" width="95" height="100" aria-hidden="true" src="loading.gif" alt=""></picture><script type="text/javascript" src="splash/splash.js"></script>
</body>

增加以下 css 样式

html { height: 100% }body {margin: 0;min-height: 100%;background-size: 100% 100%;-webkit-text-size-adjust: 100% !important;text-size-adjust: 100% !important;-moz-text-size-adjust: 100% !important;
}.center {margin: 0;position: absolute;top: 50%;left: 50%;-ms-transform: translate(-50%, -50%);transform: translate(-50%, -50%);
}

splash/splash.js 的内容如下:

function removeSplashFromWeb() {document.getElementById("splash")?.remove();document.getElementById("splash-branding")?.remove();document.body.style.background = "transparent";
}

在 Flutter main.dart 中,配置加载动画保持, 我们将在后面手动移除。

void main() {FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
}

在 AppDefere 中,移除加载动画

FlutterNativeSplash.remove();

最终效果参考下图展示:

请添加图片描述

GZIP压缩

开启gzip,压缩静态资源文件。

    gzip  on;gzip_min_length 1k;gzip_comp_level 5;gzip_vary on;gzip_static on;gzip_types text/plain text/html text/css application/javascript application/x-javascript text/xml application/xml application/xml application/json;

这里配置了压缩文件类型,如 text/plain, html,css, javascript json 等。

Gzip 压缩开启之后,可以在浏览器的开发者工具中,打开网络面板,查看响应头中,有一个 Content-Encoding: gzip 的字段,表示该文件已经被压缩。

经过下表中的采样对比可以看到,压缩率还是很高的。

文件采样压缩前压缩后压缩率
main.dart.js3.1M903k28%
vendor.js2.6M667k25%
app.js1M185k18%

CDN

也可以将静态资源放到 CDN 上,如阿里云等,通过 OSS 存储,然后配置 CDN 加速。需要注意的事,这要做好版本控制,否则会出现缓存问题。

参考资料

  • Web 渲染器
  • Flutter Web加载优化
  • How to Optimize Flutter Web and How Flutter Web work in Html Renderer
  • flutter_native_splash
  • 延迟加载组件
  • gzip压缩检测
  • Flutter for Web 首次首屏优化——JS 分片优化
  • Flutter Web 优化实践
  • Flutter 延迟加载组件

http://www.mrgr.cn/news/90087.html

相关文章:

  • 【清晰教程】通过Docker为本地DeepSeek-r1部署WebUI界面
  • AI知识库和全文检索的区别
  • 8.JVM-方法区
  • 250207-MacOS修改Ollama模型下载及运行的路径
  • 夜神NOX模拟器安装Lsposed框架以及过检测
  • 【JVM详解四】执行引擎
  • Transformer 的辉煌与大模型方向确立,点燃AGI之火把
  • 使用Redis实现业务信息缓存(缓存详解,缓存更新策略,缓存三大问题)-更新中
  • 活动预告 |【Part1】Microsoft Azure 在线技术公开课:基础知识
  • mysql8.0使用pxc实现高可用
  • 使用OBS推流,大华摄像头 srs服务器播放
  • mysql 学习14 索引
  • 【英语】考研、四六级形近词
  • 设置IDEA的内存大小,让IDEA更流畅: 建议设置在 2048 MB 及以上
  • 【02】RUST项目编译(Cargo使用)
  • LIMO:上海交大的工作 “少即是多” LLM 推理
  • 【漫话机器学习系列】087.常见的神经网络最优化算法(Common Optimizers Of Neural Nets)
  • 大数据项目2a:基于spark的电影推荐和分析系统设计与实现
  • 【滑动窗口与双指针】学习记录
  • 免费为企业IT规划WSUS:Windows Server 更新服务 (WSUS) 之更新组策略(五)
  • python学opencv|读取图像(六十)先后使用cv2.erode()函数和cv2.dilate()函数实现图像处理
  • PHP中的魔术方法
  • 在请求时打印出实际代理的目标地址
  • 机器学习分类整理【表格版】分类角度、名称、概念、常见算法、典型案例
  • 数据结构之排序
  • 线程上下文-ThreadLocal原理