背景

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

优化计划

这些问题的原因是,烘托形式为html导致的,将烘托形式由html改为canvaskit,之前遇到的问题基本就处理了,动画也不卡了,画面也流通了,图标也正常了,兼容性也提高了,再也不必担心在老板的电脑上卡住了。

烘托形式

简略说说两种形式的差异。
html烘托形式:flutter会选用HTML的custom element,CSS,CanvasSVG来烘托UI元素。
canvaskit烘托形式:flutter将 Skia 编译成 WebAssembly 格式,并运用 WebGL 烘托。

html canvaskit
命令行 –web-renderer html –web-renderer canvaskit
优点 体积更小 烘托功能强;多端一致
缺点 烘托功能差;跨端兼容差 体积相较html多2.5M

所以运用canvaskit会愈加流通,更符合FLutter的气质。可是!也呈现了些新的问题。

由Canvaskit引起的问题

图片跨域

报错描述: Access to XMLHttpRequest at ” 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便是图片本身,不会呈现问题。

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

不会呈现跨域的问题原因是在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,不支撑跨域。而我的图片地址和服务地址并不在一个域名,所以呈现该问题。

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

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

那怎么处理呢?

询问前端大佬后,发现处理问题最快的办法便是放到本身服务的域名下。随后我把图片放在项目中的asset目录中,更改本地引证地址,打包上传布置,处理!

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

初次翻开加载慢

在初次改成canvaskit形式布置后,翻开网站,页面一度白屏很长时刻,预计有10秒,检查后台日志发现是下载了很多文件,包含canvaskit制作引擎、字体等。首要耗时是在引擎(约9M)、字体下载,而下载这些的域名都是官方的,所以下载速度也有所限制。

我把FlutterWeb渲染模式改成Canvaskit后...
处理办法: 将引擎和字体传值自己的服务器,以加快下载速度。

  1. 引擎本地化,检查网络恳求概况,能够看到下载地址,单独下载后放到项目中。

    我把FlutterWeb渲染模式改成Canvaskit后...
    我的方位是web/assets/canvaskit/canvaskit.js&wasm
    我把FlutterWeb渲染模式改成Canvaskit后...

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

    --dart-define=FLUTTER_WEB_CANVASKIT_URL=assets/canvaskit/
    
  2. 本地化加载KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf字体文件,同样在恳求概况中获取地址,下载至本地,放在本地,web/assets/canvaskit/

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

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

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

    此外在评论中有主张在本地设置字体包的办法,这样不必替换也能够防止用光放域名下载,更方便。如图所示:

    我把FlutterWeb渲染模式改成Canvaskit后...
    原因是因为Flutter会把Roboto字体设置为兜底的备用字体,如果不设置它,它永久会去font.gstatic.com上fetch这个字体。

字体需下载

在翻开页面时,会呈现字体乱码,原因是Skia 自绘引擎需求字体库支撑,它正在下载字体,并且引证的字体不一样下载的库也是不同的。同样也能够下载至本地,替换main.dart.js的地址,但下载完体会后,发现不管是第一次仍是之后都会呈现乱码,仅仅显现的时刻长短,体会也是不很好。

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

我把FlutterWeb渲染模式改成Canvaskit后...
所以我是在pubspec.yaml中设置了本地的字体包的办法处理的,这样在初次加载或后边的改写,都未呈现过乱码。

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

main.dart.js 切片化

以上两步仅仅加快了下载速度,但所需求下载的内容巨细没变,好在Flutter 官方供给deferred as关键字来完成 Widget 的懒加载,而 dart2js 在编译过程中能够将懒加载的 Widget 进行按需打包,这样的拆包机制叫做 Lazy Loading。借助 Lazy Loading,咱们能够在路由表中运用 deferred 引进各个路由(页面),以此来到达业务代码拆离的目的。具体的代码请看这篇文章《JS 分片优化》,很具体。

我通过拆分后,main.dart.js由8.5M缩减至5.5M。

加载时提示

通过上面三步设置,初次加载时长会有大大缩减,可是也会有白屏,为了更好的体会在白屏时加个提示。

// 在 web/index.html 中的 head 标签下加提示
<style>
    body {
          width: 100vw;
          height: 100vh;
          display: flex;
          justify-content: center;
          align-items: center;
      }
</style>
// 在 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),//(本次新增的办法)
      .....
      ))

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

最终

Canvaskit形式尽管动画、交互体会好,但相较于Html形式,Canvaskit的加载速度是一个劣势,因为我做的是内部运用的网站,加载速度和交互体会之间我选择了后者。

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

引荐保藏

  1. Flutter Web 在《一同漫部》的功能优化探索与实践

参考

  1. Flutter web内网网站怎么发布?处理外网下canvaskit.js和字体无法加载问题
  2. serviceWorker 服务器与浏览器之间的署理
  3. Flutter 敞开web构建以及web的两种烘托形式