背景
在使用Flutter Web开发的网站过程中,常常会遇到不同浏览器之间的兼容性问题。例如,在Google浏览器中动画和交互都非常流畅,但在360浏览器中却会出现卡顿现象;在Google浏览器中动态设置图标颜色正常显示,而在Safari浏览器中颜色会缺失变成黑色;甚至在某些电脑的Google浏览器中也会出现动画卡顿和页面报错的问题。
优化方案
这些问题的根源在于渲染模式的选择。将渲染模式从HTML改为CanvasKit后,大部分问题得以解决,动画变得流畅,画面也变得顺滑,图标显示正常,兼容性也得到了提升。
渲染模式
- HTML渲染模式:Flutter使用HTML的
custom element
、CSS
、Canvas
和SVG
来渲染UI元素。 - CanvasKit渲染模式:Flutter将Skia编译成
WebAssembly
格式,并使用WebGL
进行渲染。
HTML | CanvasKit | |
---|---|---|
命令行 | –web-renderer html | –web-renderer canvaskit |
优点 | 体积更小 | 渲染性能强,多端一致 |
缺点 | 渲染性能差,跨端兼容差 | 体积相较HTML多2.5M |
尽管CanvasKit模式提供了更流畅的体验,但它也带来了一些新的问题。
由CanvasKit引起的问题
图片跨域
报错描述:
Access to XMLHttpRequest at ‘https://…/icon/setting_228.webp’ from origin ‘https://…’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
在CanvasKit模式下,图片请求类型变为xhr
,不支持跨域,导致跨域问题。解决方法是将图片放到与服务同域的目录下。
首次打开加载慢
首次加载CanvasKit模式的网站时,会下载大量文件,包括CanvasKit绘制引擎和字体。解决方法是将引擎和字体文件存放到自己的服务器,以加快下载速度。
-
引擎本地化:下载引擎文件并放到项目中,然后在运行或打包时指定本地路径。
--dart-define=FLUTTER_WEB_CANVASKIT_URL=assets/canvaskit/
-
字体本地化:下载字体文件并放到本地,替换构建后的main.dart.js中的字体路径。
字体需下载
Skia自绘引擎需要字体库支持,导致首次加载时出现字体乱码。解决方法是在pubspec.yaml中设置本地字体包。
main.dart.js 切片化
以上两步只是加快了下载速度,但所需要下载的内容大小没变,好在Flutter 官方提供 deferred as
关键字来实现 Widget 的懒加载,而 dart2js 在编译过程中可以将懒加载的 Widget 进行按需打包,这样的拆包机制叫做 Lazy Loading。借助 Lazy Loading,我们可以在路由表中使用 deferred 引入各个路由(页面),以此来达到业务代码拆离的目的。具体的代码请看这篇文章《JS 分片优化》,很详细。
试验拆分后,main.dart.js由8.5M缩减至5.5M。
加载时提示
为了解决首次加载时白屏的问题,可以在白屏时加个提示。
html复制
<style>body {width: 100vw;height: 100vh;display: flex;justify-content: center;align-items: center;}
</style>
<div id="text">静态资源加载中...</div>
浏览器刷新后页面加载两次
刷新页面时会加载两次,是由于serviceWorker注册失败导致。解决方法是注释掉注册逻辑,直接调用loadMainDartJs()
。
路由包装url地址方式失效
在CanvasKit模式下,刷新后不会停留在当前页面。解决方法是在刷新时记录当前页面,并在初始化时还原。
最后
尽管CanvasKit模式在动画和交互体验上优于HTML模式,但其加载速度较慢。在内部使用的网站中,可以优先考虑交互体验。
参考
- Flutter web内网网站如何发布?解决外网下canvaskit.js和字体无法加载问题
- serviceWorker 服务器与浏览器之间的代理
- Flutter 开启web构建以及web的两种渲染模式