[AliFlutter]Flutter for Web在无影中的应用

本文涉及的产品
无影云电脑企业版,4核8GB 120小时 1个月
无影云电脑企业版,4核8GB 120小时 1个月
无影云电脑个人版,黄金款:40核时/1个月有效
简介: 无影是使用Flutter的重度用户,无论是在成熟的移动Android、iOS上,还是桌面端MacOS、Windows、还有各种硬件终端上(Linux)上都有应用![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/c2434612-86ee-4fb6-a7d1-1622eb6d050d.png)今年无影使用Flutt

无影是使用Flutter的重度用户,无论是在成熟的移动Android、iOS上,还是桌面端MacOS、Windows、还有各种硬件终端上(Linux)上都有应用

今年无影使用Flutter的端又新添了一名成员—浏览器。很兴奋的告诉大家,我们已经将Flutter应用到了Flutter能够覆盖的所有场景。

去年11月我开始研究Flutter for Web的可行性,部分内容我不再复述,大家自行查看这篇内容了解),12月初开始,我就投入了大部分精力到无影桌面端转Web上了,到今年1月初,Flutter转Web工程开始提测,2月9日,发布了第一个线上版本(https://wuying.aliyun.com/v3 )。 这篇文章给大家分享我们的改造过程及踩坑过程。

背景

在使用Flutter for Web前,我们无影客户端是桌面端和Web客户端各自开发维护。桌面端采用Flutter开发,Web端使用React开发,同时桌面端和web端的功能也几乎一致。还有,我们无影是一个高速发展的项目,功能迭代很快,每个版本都会新增大量功能以及会有大量功能的重构,这从下面的图就可以看出来。去年7月份我们重构了UI发布了5.0版本,到去年11月的时候,又发布了为云栖打造的新的交互版本。

由于使用两套代码开发,这给我们项目的迭代带来了很大的挑战,我们需要大量人力去做开发,同时还存在两端发布时间间隔较长、功能不一致等问题,而如果能将桌面端代码直接复用到Web端,就能完美解决这些问题,这也促使我们花费更多的精力去研究Flutter for Web。

面临的挑战

虽然Flutter for Web在Flutter2.0版本就已经合入主线,但还是存在很多问题。

我们选择了canvaskit渲染模式,因为html渲染模式还存在一些解不了的问题:

  1. html渲染模式存在很多兼容性问题,部分Flutter API直接无法使用,比如BlendMode.clear,在html渲染模式中使用会直接报错,我们项目中还大量使用了该API,且没有找打其它方式去替代。
  2. html模式渲染效果部分地方同桌面端存在差异
  3. html渲染模式在列表滚动、动画下渲染效果十分糟糕,我们的客户端主页里正好存在这些case(同时存在Flutter布局更新及DOM的布局更新导致渲染性能下降)

因为这些原因,我们放弃了使用html模式,使用了canvaskit模式。不过canvaskit模式也依然存在部分问题

  1. Flutter for Web本身的编译产物就很大,而canvaskit渲染模式还需要额外引入canvaskit.wasm,极大增加了首屏渲染下载资源的大小
  2. canvaskit使用WebGL渲染,WebGL无法加载系统字体,加载额外的字体文件也给首屏渲染增加负担
  3. canvaskit渲染模式使用了WebAssembly和WebGL,所以存在一些兼容性问题。

同时,还面临了一些不可避免的挑战

  1. 基础功能如日志、埋点、存储等需要在Web侧进行支持
  2. 工程化生态不足,构建、部署及静态资源处理都需要从0到1支持
  3. 存在一些dart:html提供的标准API兼容性问题,如退出全屏document.exitFullscreen不是所有浏览器都兼容

功能适配

  1. 桌面端已有功能适配

我们桌面端存在大量插件来扩展Flutter的能力,所以在Web端,我们必须逐一去分析插件的能力然后使用Web的方式去兼容,兼容方式可以看看我上篇文章平台兼容,这里工作量巨大且容易遗漏,只能靠测试来保证功能完整,存在一些风险。

  1. JS侧已有功能适配

集团有很多JavaScript版本的库,在dart中并没有相应的版本,而桌面端使用的是C++实现的,所以需要进行适配。比如我们的埋点库,其架构图如下

我们根据dart版本的能力抽象出tracer_interface层,然后在JS中找到对应能力并抽象出JS_interface层,其用于对接三方库,然后采用Dart调用JavaScript来对接JavaScript的SDK。

首屏加载优化

刚刚说了首屏资源过大造成资源下载时间变长会极大降低用户使用的意愿。经过一系列探索和试错,我总结了以下优化首屏资源的方案。

文字大小处理

canvaskit渲染模式是采用的webgl渲染,webgl渲染无法使用系统字体,必须加载额外的字体文件,我们使用的是阿里普惠体,完整的阿里普惠体有8MB的大小,我们需要优化这个字体的大小。这里我使用了工具fontTools将常用的3000多个字符包括字母及符号输出到一个大约800KB的文件中,然后首屏渲染时只加载这个优化的字体文件,当首屏渲染完成后,再使用Flutter提供的FontLoader去动态加载完整的字体文件。

动态加载字体:

var bundle = await rootBundle.load('assets/fonts/xxx.ttf');
var loader = FontLoader('Alibaba PuHuiTi')..addFont(Future.value(bundle));
await loader.load();

MaterialIcon图标文件处理

由于历史原因,我们项目中使用了部分Material的图标,因此在编译时会将完整的MaterialIcons-Regular.otf文件(1.6MB)放到fonts目录并在首次渲染时加载,万幸Flutter提供了工具(传入--tree-shake-icons),可以将这个文件精简,只包含项目中使用到的图标。然而在Flutter3.0.2版本中编译web时传入这个参数会导致编译错误,最新master分支已支持),我们可以通过flutter build apk --tree-shake-icons编译Android的方式来获得这个优化的MaterialIcons-Regular.otf文件,然后在Web的编译产物中替换这个文件。

使用deferred as拆分文件

Flutter会将整个工程编译到一个文件mian.dart.js文件中,我们可以使用deferred as来拆分文件,将文件拆分为mian.dart.js_1.part.jsmian.dart.js_2.part.js等,这样就减少了首次加载文件的大小。

支持Gzip压缩

Gzip压缩跟项目本身的设置没有关系,它需要网站部署的服务去支持,这个步骤对大文件的优化效果很好。顺便提一下,我们的前端使用的是O2 Space部署,目前它对wasm格式的文件没有支持Gzip压缩,这里我通过将canvaskit.wasm改为canvaskit.txt来支持canvaskit文件的Gzip压缩(希望官方尽快支持)。

使用加载动画

我使用了Flutter提供的加载函数并使用了JavaScript及CSS做了一个加载动画盖在Flutter屏幕上面,当Flutter界面加载完成并显示时,通知JS隐藏加载动画来达到一个无痕切换。

经过这一系列优化后,我们无影Web客户端从刚开始首屏加载需要23MB资源到最后只需要加载6.6MB资源,需要说明的是,我们的项目也提供了静态资源缓存,所以只有用户首次访问我们的网站是需要下载这些资源,后续再次访问时能从缓存中获得更快的加载速度。

其它疑难问题

  1. 后备字体处理

canvaskit模式需要加载字体的特性,让Flutter官方团队为了保证所输入的字体都能正常显示,引入了后备字体的概念,当输入的字符缺少字体引入时,会从google字体库fonts.gstatic.com下加载后备字体。我们在测试中发现,即使我们加载了完整的阿里普惠体文件,Flutter还是会去加载后备字体,而如果下载后备字体网络遇到问题,首屏加载的速度非常慢。后来我测试出是我们的字体库中没有零宽字符(#8203)导致。国内因为一些众所周知的原因,访问fonts.gstatic.com有很大概率出现问题,所以只能修改引擎代码,将Flutter加载后备字体的代码去掉。

// 注释flutter/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart中的resolvedFonts.forEach(notoDownloadQueue.add);
// resolvedFonts.forEach(notoDownloadQueue.add);

然后重新编译flutter engine。

使用自定义Flutter Web Engine的方式:最好的方式当然是搭建Flutter Engine源,不过成本太高。我们目前采用的方式是将重新编译的引擎中的 {engine路径}/src/out/host_debug_unopt/flutter_web_sdk文件夹替换 {flutter路径}/bin/cache/flutter_web_sdk,然后将这个编译后的 flutter_web_sdk压缩后团队内共享。
  1. 去除浏览器默认样式

在Safari、FireFox等浏览器中,会存在一些默认样式,比如密码输入框后面会出现自带的图标

如果我们要去除这种默认样式,直接写外联css不会生效,需要在flt-glass-pane下写css来清除。JS代码如下

const parent = document.querySelector('flt-glass-pane')
const showDom = parent.shadowRoot
let style = document.createElement('style');
style.innerHTML = '::-ms-reveal { display: none; } ';
showDom.appendChild(style);

部署

部署流程

上面提到的我们部署Flutter产物使用的O2 Space,部署流程如下:

我新建了github仓库用来放置Flutter编译的web产物,目录结构大概如下

├─ .gitignore
├─ .npmrc
├─ abc.json # O2 Space部署脚本
├─ gulpfile.js # 针对Flutter产物二次编译
├─ package-lock.json
├─ package.json
└─ web # Flutter构建产物
       ├─ .last_build_id
       ├─ assets
       ├─ entry.json
       ├─ favicon.png
       ├─ flutter.js
       ├─ flutter_service_worker.js
       ├─ icons
       ├─ index.html
       ├─ main.dart.js
       ├─ main.dart.js_1.part.js
       ├─ manifest.json
       ├─ schedule.css
       ├─ schedule.js
       └─ version.json

当执行flutter build web后,将构建的web产物上传到该仓库中,gulpfile.js会对产物二次编译,比如清除代码注释、JS代码混淆等,只要我们配置好abc.json这些事情会在O2 Space部署时自动完成。

使用CDN静态资源

Flutter加载的静态资源默认需要和index.html在同一目录下。我们可以通过下面方式分别设置静态资源和JavaScript

  1. 静态资源(除JavaScript外):我们可以通过在index.html中设置meta为assetBase的标签来改变项目中加载图片、字体、json文件等资源,如<meta name="assetBase" content="https://dev.g.alicdn.com/aliyun/cdn/0.0.1/"/>。这部分可以在gulpfile.js中动态替换以达到开发环境和部署环境区分开。
  2. JavaScript:因为Flutter加载JS使用的appendChild来加载带src的script标签,我们可以通过重载document.body.appendChild函数来达到更改src路径的目的。
let cdnURL = document.querySelector('meta[name="assetBase"]') ? .getAttribute('content') ? ? '/';
let appendChild = document.body.appendChild
document.body.appendChild = function (el) {
  return appendChild.call(document.body, function () {
    {
      if (cdnURL !== '/' && el.nodeName.toLowerCase() === 'script' && el.baseURI !== cdnURL) {
        el.src = el.src.replace(el.baseURI, cdnURL)
      }
      return el
    }
  })
}

效果展示

总结

Flutter for Web对于无影目前的情况来说非常合适,不过还是有一些未解决的问题:

  1. 首屏加载资源依然很大,需要持续优化
  2. 主页长时间静置偶现崩溃,未找到具体原因
  3. 部署流程未全部自动化,有出错风险
  4. 无法方便的使用JS生态,需要手动兼容
  5. 字体问题需要自定义引擎

针对这些问题,我也在我们Flutter for Web工程上提出了一些展望

  1. 将首屏资源加载大小控制在5MB以内
  2. 构建部署docker,将部署流程自动化
  3. 编写工具自动生成JS和Dart接口,方便Dart对接JS生态
  4. 与官方共建,找到更完美使用后备字体的方式
目录
相关文章
|
17天前
|
SQL 缓存 搜索推荐
后端技术在现代Web开发中的应用与挑战
本文将深入探讨后端技术在现代Web开发中的重要性,涵盖从基础架构到性能优化的多个方面。通过分析当前主流后端技术的优缺点,并提供一些实用的解决方案和建议,帮助开发者更好地应对日常开发中的挑战。
33 1
|
10天前
|
传感器 缓存 监控
Stream 组件在 Flutter 中的应用场景有哪些?
Stream 组件在 Flutter 中的应用场景有哪些?
156 58
|
15天前
|
人工智能 运维 云计算
阿里云无影AI云电脑亮相 体验大幅升级
9月20日,2024云栖大会上阿里云无影AI云电脑全新亮相,基于最新的终端云计算技术和AI大模型能力,无影的综合体验大幅提升,新增了弹性升降配、双网自由切换、多端操作系统知识库问答、编码大师等AI智能体功能,为安全办公、个人娱乐带来全新的云上流畅体验,更可畅玩《黑神话:悟空》等3A游戏大作。同时,无影还宣布向开发者全面开放应用中心生态,开发者可免费入驻。
127 15
|
15天前
|
开发框架 搜索推荐 开发工具
打造个性化安卓应用:从零开始的Flutter之旅
【8月更文挑战第51天】本文是一篇面向初学者的Flutter入门教程,旨在通过简单易懂的语言和实际代码示例,引导读者步入跨平台移动应用开发的世界。文章首先介绍了Flutter的基本概念和优势,然后逐步展示了如何搭建开发环境、创建第一个Flutter应用,并实现了一个简单的待办事项列表。最后,文章探讨了Flutter在实现高性能和美观界面方面的潜力,鼓励读者发挥创意,探索更多可能。
66 15
|
7天前
|
Web App开发 JavaScript 前端开发
构建高效Web应用:Node.js与Express框架的深度整合
【9月更文挑战第28天】在现代Web开发领域,Node.js和Express框架的结合已成为打造高性能、易扩展应用的黄金组合。本文将深入探讨如何利用这一技术栈优化Web应用架构,提供具体实践指导,并分析其性能提升的内在机制。通过代码示例,我们将展示从基础搭建到高级功能的实现过程,旨在为开发者提供一条清晰的学习路径,以实现技术升级和项目效率的双重提升。
20 3
ly~
|
9天前
|
存储 监控 小程序
除了 Web 开发,PHP 还可以应用于哪些领域?
PHP 在 Web 开发之外还有多个应用场景:1)命令行脚本,如批量处理文件、数据库管理及系统监控;2)利用 PHP-GTK 等工具开发桌面应用,满足特定业务需求;3)结合微信云开发功能支持微信小程序后端,处理数据存储与用户认证;4)为小型游戏或特定类型游戏开发游戏服务器逻辑;5)在物联网领域作为后端语言处理设备数据交互与分析。
ly~
25 4
|
9天前
|
JavaScript 前端开发 UED
WebSocket在Python Web开发中的革新应用:解锁实时通信的新可能
在快速发展的Web应用领域中,实时通信已成为许多现代应用不可或缺的功能。传统的HTTP请求/响应模式在处理实时数据时显得力不从心,而WebSocket技术的出现,为Python Web开发带来了革命性的变化,它允许服务器与客户端之间建立持久的连接,从而实现了数据的即时传输与交换。本文将通过问题解答的形式,深入探讨WebSocket在Python Web开发中的革新应用及其实现方法。
23 3
|
9天前
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
在快速发展的Web开发领域,高性能与高效响应是衡量应用质量的重要标准。随着Python在Web开发中的广泛应用,如何利用Python的协程(Coroutine)与异步函数(Async Functions)特性来优化Web应用的性能,成为了许多开发者关注的焦点。本文将从实战角度出发,通过具体案例展示如何运用这些技术来提升Web应用的响应速度和吞吐量。
12 1
|
13天前
|
中间件 API 开发者
深入理解Python Web框架:中间件的工作原理与应用策略
在Python Web开发中,中间件位于请求处理的关键位置,提供强大的扩展能力。本文通过问答形式,探讨中间件的工作原理、应用场景及实践策略,并以Flask和Django为例展示具体实现。中间件可以在请求到达视图前或响应返回后执行代码,实现日志记录、权限验证等功能。Flask通过装饰器模拟中间件行为,而Django则提供官方中间件系统,允许在不同阶段扩展功能。合理制定中间件策略能显著提升应用的灵活性和可扩展性。
15 4
|
23天前
|
JSON Dart Java
flutter开发多端平台应用的探索
flutter开发多端平台应用的探索
27 6

相关产品

  • 无影云电脑
  • 下一篇
    无影云桌面