Flutter 的跨平台一致性,到了浏览器中会遇到什么变化?郭树煜在这场分享里把问题拆成两部分:产物怎样加载,以及界面最终怎样绘制。前者影响首次打开的成本,后者解释为什么看起来相似的组件,可能生成不同结构,甚至出现不同的文本效果。
这段分享来自深圳“前端的挑战与机遇”录播第 4 段,所属稿件发布于 2022 年 5 月 9 日。以下讨论针对分享时的 Flutter Web 实现,特别是当时的 HTML 渲染器,不是当前版本的配置指南。
相同框架,进入浏览器后仍有不同实现
郭树煜先回顾 Flutter 与 Web 技术的渊源,再与先有 Web 框架、后扩展客户端支持的跨端方案作比较。Flutter 先在移动端形成框架与渲染体系,再进入浏览器,因此不是把现成网页组件简单搬到手机的反向过程。
共用框架 API 带来了复用可能,但宿主能力并不一致,绘图等接口尤其需要适配。片中介绍当时的两条路径:HTML 渲染器更多借助浏览器元素及接口,CanvasKit 则使用 WebAssembly 与 Skia 相关能力,追求更接近移动端的绘制一致性。前者与 Web 能力耦合更深,后者需要承担额外资源体积与加载成本。原片 00:26—05:11
先看产物,而不是笼统地说包大
他以自己的项目展示当时的默认构建:除了主 JavaScript 文件,还可能加载渲染器资源和图标字体。不同平台的默认选择以及是否同时包含多种渲染路径,都会改变产物构成。
因此,第一步是确认项目实际需要哪些资源。在分享的方案中,他移除未使用的图标依赖,明确选择一种渲染器,再逐项检查剩余体积。选择 HTML 路径是其当时对可控性与优化空间的判断,并不意味着所有应用都应作相同选择。原片 05:12—08:03
图标裁剪、延迟加载与传输压缩分别解决什么
图标字体通常包含比应用实际使用更多的内容。郭树煜介绍过构建时移除未用图标的方式,也记录了当时 Web 构建遇到裁剪问题、借助另一平台构建结果处理字体的临时做法。这是历史版本问题的应对,不能当作今天应复制的固定步骤。原片 08:03—09:00
主 JavaScript 则通过 Dart 的延迟导入思路拆分:将不必立即使用的内容延后,在需要时加载对应代码。片中展示了 deferred as 与 loadLibrary() 的使用方向,以及编译后出现的分块文件。它把一部分下载从首屏移到后续使用时,并没有让那部分功能永远不占资源。原片 09:00—10:22
随后,他用 source map 分析产物,区分业务部分与框架、引擎相关部分。不是所有字节都值得通过修改框架来消除,还应考虑优化收益和维护成本。最后再通过 Gzip 减少传输体积。演示中从兆字节降到数百 KB 的数据,涉及不同处理阶段与传输口径,不是所有项目都能取得的固定比例。原片 10:23—12:51
HTML 渲染器不等于所有内容都用普通标签
后半场进入具体实现。Flutter 框架中的 Canvas 接口由平台对应实现承接;在当时 Web 路径中,郭树煜主要分析 SurfaceCanvas,并继续区分 DomCanvas 与 BitmapCanvas。
他先观察一个滚动列表:页面中出现以 flt- 开头的自定义元素,离开可见区域的项目与可见项目内部结构不同。更值得注意的是,文本不一定表现为普通的段落标签,也可能画在 Canvas 上。不能因为选择了 HTML 渲染器,就以为页面结构会与手写网页相同。原片 12:51—15:28
在他的分析中,DomCanvas 主要通过 HTML 元素承接绘制;BitmapCanvas 的路径也不是始终只画 Canvas,还会因具体能力与兼容条件转向元素。理解分支,比只记住渲染器名字更能解释实际输出。原片 15:29—17:51
同一段文字,加背景后为什么换了画法
第一个连续示例从简单文本开始:当时实现把它绘制到 Canvas。增加红色背景后,结果却变成了段落和行内元素。
郭树煜沿绘制矩形、再绘制文本的路径解释这种变化。背景先触发了元素绘制,当前绘制状态又影响后续段落的处理,最终让文本也进入元素路径。也就是说,文本怎样显示不仅取决于 Text 自己的属性,还可能与同一绘制过程中的其他操作有关。原片 17:51—22:03
这一点对排查很重要:遇到文字清晰度或输出结构变化时,仅比较字符串和字体参数,可能还没有覆盖真正变化的条件。
阴影、滤镜与矩阵,继续改变分支
在文字和红色背景上加阴影后,演示中的输出重新回到 Canvas 路径。再加入颜色滤镜,又转为元素绘制。郭树煜把原因追到过滤参数与宿主兼容性:当某些效果不能可靠地通过目标浏览器的 Canvas 路径呈现时,引擎选择其他方式承接。
复杂矩阵变换也有相似情况。片中用带立体效果的变换展示,输出不再只按最初的简单绘制逻辑处理。这些例子的共同点是:引擎需要在 Flutter 的效果表达与浏览器实际能力之间协调,分支反映的是当时的实现和兼容条件。原片 22:04—24:46
背景走 Canvas,文本仍然可能走 DOM
接着,郭树煜撤掉前面的滤镜与复杂变换,在带阴影的场景给文本加装饰线。结果是背景仍由 Canvas 绘制,文本却变成段落与行内元素。
他进一步提到字形特性等条件:某些文字功能利用 HTML 与 CSS 更便于表达,因此文本自己的判断也会改变最终路径。父级走 Canvas,并不能推出所有子内容一定走 Canvas。原片 24:48—26:38
去掉文本,再看简单图形与图层
最后几个示例把文本移除,只保留简单矩形及相关样式,以观察何时进入 DomCanvas。郭树煜随后把前面的分支重新串起来:绘制操作、滤镜、变换和文本特性一起决定走向,不能用单个属性概括所有情况。
他又把页面中的 flt- 自定义元素与图层操作关联起来。例如添加 picture 会留下相应层级,层内部再选择 Canvas 或元素完成具体呈现。理解这层关系后,就能从浏览器看到的结构追到渲染过程,而不只是面对陌生标签猜测。原片 26:38—31:49
分享最后回到实用目的:构建优化改善加载,理解绘制路径则帮助解释表现差异,尤其是特定分辨率或条件下的文本清晰度。不同平台与版本可能作不同选择,排查应结合实际输出和实现,而不是认为所有端天然完全一致。本分段以总结结束,未收录现场问答。原片 31:50—32:50
