注册

我把FlutterWeb渲染模式改成Canvaskit后...

背景


用FLutterWeb开发的网站在使用过程中出现了一些问题,比如在Google浏览器中使用交互、动画流畅,在360浏览器中就卡顿;图标在代码中动态设置颜色的方式在Google浏览器中正常显示,在Safari浏览器中颜色缺失,变为黑色;在有的电脑中Google浏览器也有动画、交互卡顿的现象、页面报错等。很奇怪,一脑袋问号。


优化方案


这些问题的原因是,渲染模式为html导致的,将渲染模式由html改为canvaskit,之前遇到的问题基本就解决了,动画也不卡了,画面也流畅了,图标也正常了,兼容性也提高了,再也不用担心在老板的电脑上卡住了。


渲染模式


简单说说两种模式的区别。

html渲染模式:flutter会采用HTML的custom element,CSS,CanvasSVG来渲染UI元素。

canvaskit渲染模式:flutter将 Skia 编译成 WebAssembly 格式,并使用 WebGL 渲染。



























htmlcanvaskit
命令行--web-renderer html--web-renderer canvaskit
优点体积更小渲染性能强;多端一致
缺点渲染性能差;跨端兼容差体积相较html多2.5M

所以使用canvaskit会更加流畅,更符合FLutter的气质。但是!也出现了些新的问题。


由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.



在html模式下是正常的,而在canvaskit出现了跨域问题,查看日志发现区别。


html的网络请求类型type就是图片本身,不会出现问题。
图片跨域2.png



不会出现跨域的问题原因是在HTML中,有一些标签也可以发起HTTP请求,比如script标签,link标签,img标签,form标签,且被允许跨域。



  1. link,img标签都是单纯的引入资源文件
  2. form标签用于收集用户输入并发送,但是发送成功会跳转到新网页,并将服务器响应作为新网页的内容
  3. script标签可以引入外部js文件,并执行引入的js文件的代码

其中,script标签由于其可以执行引入的js文件的代码,再加上其跨域特性,让script标签可以用来做一些超出其设计初衷的事。script标签会发起HTTP GET去请求服务器上的js文件,所以script标签可以用于实现HTTP GET跨域请求。



而canvaskit模式下,请求类型是xhr,不支持跨域。而我的图片地址和服务地址并不在一个域名,所以出现该问题。
图片跨域1.png



原因是同源策略,它是浏览器特有的一种安全机制,主要用于限制不同的源之间的数据交互。



那如何解决呢?


询问前端大佬后,发现解决问题最快的方法就是放到自身服务的域名下。随后我把图片放在项目中的asset目录中,更改本地引用地址,打包上传部署,解决!


(PS:这个问题在本地debug模式下,并不会出现)


首次打开加载慢


在首次改成canvaskit模式部署后,打开网站,页面一度白屏很长时间,预计有10秒,查看后台日志发现是下载了很多文件,包括canvaskit绘制引擎、字体等。主要耗时是在引擎(约9M)、字体下载,而下载这些的域名都是官方的,所以下载速度也有所限制。
引擎下载.png
解决办法:
将引擎和字体传值自己的服务器,以加快下载速度。




  1. 引擎本地化,查看网络请求详情,可以看到下载地址,单独下载后放到项目中。
    下载引擎.png
    我的位置是web/assets/canvaskit/canvaskit.js&wasm
    引擎存放地址.png

    再设置替换引擎路径,在运行或打包的时候加上以下命令行。等号后面为本地的路径。


    --dart-define=FLUTTER_WEB_CANVASKIT_URL=assets/canvaskit/



  2. 本地化加载KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf字体文件,同样在请求详情中获取地址,下载至本地,放在本地,web/assets/canvaskit/


    字体下载.png


    替换本地地址,在构建完成后的build目录下的main.dart.js中搜索该字体名,把前缀替换成本地路径。


    https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf
    替换成
    assets/canvaskit/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf



字体需下载


在打开页面时,会出现字体乱码,原因是正在下载字体,而且引用的字体不一样下载的库也是不同的。同样也可以下载至本地,替换main.dart.js的地址,但下载完体验后,发现不管是第一次还是之后都会出现乱码,只是显示的时间长短,体验也是不很好。

字体乱码.png


下载字体地址.png
所以我是在pubspec.yaml中设置了本地的字体包的方式解决的,这样在首次加载或后面的刷新,都未出现过乱码。


设置字体.png


加载时提示


经过上面两步设置,首次加载时长会有大大缩减,但是也会有白屏,为了更好的体验在白屏时加个提示。


// 在 web/index.html 中的 body 标签下加提示
<div id="text">静态资源加载中...</div>

浏览器刷新后页面加载两次


在使用网站时刷新会出现页面加载两次的问题,查看日志发现是web/index.html中的一段代码引起的。


// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);

引起超时的原因是navigator.serviceWorker.register(serviceWorkerUrl)注册失败,而上面的代码是兜底的逻辑。serviceWorker是服务器与浏览器之间的代理,目前用不上,所以将注册逻辑注释掉,直接调用loadMainDartJs()即可。


路由包装url地址方式失效


在canvaskit模式下,刷新后不会停留在当前页面了。之前写过一篇文章《FlutterWeb浏览器刷新后无法回退的解决方案》中的方案看来只适应在html模式下。


解决办法:
在上面的的文章基础上稍微修改下。


 // 刷新时回调
_beforeUnload = (event) {
// 本地记录,标记成"已刷新"
DB(DBKey.isRefresh).value = true;

// 记录刷新时的页面,用于还原(本次新增的方法)
List history = get();
DB(DBKey.initRoute).value = history.last;
history.removeLast();
set(history);

// 移除刷新前的实例的监听
html.window.removeEventListener('beforeunload', _beforeUnload);
html.window.removeEventListener('popstate', _popState);
};

// 获取上次最后的页面,(本次新增的方法)
static String initRoute(currentContext) {
return DB(DBKey.initRoute).get(Uri(scheme: RoutePath.scheme, host: RoutePath.home).toString());
}

// 初始化
MaterialApp(
.....
initialRoute: RouterHistory.initRoute(context),//(本次新增的方法)
.....
))

这样设置完后也会停留在当前页面了。


最后


如果有遇到其他问题或更好的解决办法欢迎提出讨论


作者:苏啵曼
链接:https://juejin.cn/post/7212101192746303544
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册