随着 Flutter 3.10 发布,Flutter Web 也引来了它最具有「里程碑」意义的更新,这里的「里程碑」不是说这次 Flutter Web 有多么重大的更新,而是 Flutter 官方对于 Web 终于有了明确的定位和方向。
提升
首先我们简单聊提升,这不是本篇的重点,只是顺带。
本次提升主要在于两个大点:**Element 嵌入支持和 fragment shaders 支持 **。
首先是 Element 嵌入,Flutter 3.10 开始,现在可以将 Flutter Web嵌入到网页的任何 HTML 元素中,并带有 flutter.js
引擎和 hostElement
初始化参数。
简单来说就是不需要 iframe
了,如下代码所示,只需要通过 initializeEngine
的 hostElement
参数就可以指定嵌入的元素,灵活度支持得到了提高。
<html>
<head>
<!-- ... -->
<script src="flutter.js" defer></script>
</head>
<body>
<!-- Ensure your flutter target is present on the page... -->
<div id="flutter_host">Loading...</div>
<script>
window.addEventListener("load", function (ev) {
_flutter.loader.loadEntrypoint({
onEntrypointLoaded: async function(engineInitializer) {
let appRunner = await engineInitializer.initializeEngine({
// Pass a reference to "div#flutter_host" into the Flutter engine.
hostElement: document.querySelector("#flutter_host")
});
await appRunner.runApp();
}
});
});
</script>
</body>
</html>
PS :如果你的项目是在 Flutter 2.10 或更早版本中创建的,要先从目录中删除
/web
文件 ,然后通过flutter create . --platforms=web
重新创建模版。
fragment shaders 部分一般情况下大家可能并不会用到,shaders 就是以 .frag
扩展名出现的 GLSL 文件,在 Flutter 里是在 pubspec.yaml
文件下的 shaders
中声明,现在它支持 Web 了:
flutter:
shaders:
- shaders/myshader.frag
一般运行时会把 frag 文件加载到
FragmentProgram
对象中,通过 program 可以获取到对应的shader
,然后通过Paint.shader
进行使用绘制, 当然 Flutter 里 shaders 文件是存在限制的,比如不支持 UBO 和 SSBO 等。
当然,这里不是讲解 shaders ,而是宣告一下,Flutter Web 支持 shaders 了。
未来
其实未来才是本篇的重点,我们知道 Flutter 在 Web 领域的支持上一直在「妥协」,Flutter Web 在整个 Flutter 体系下一直处于比较特殊的位置,因为它一直存在两种渲染方式:html 和 canvaskit。
简单说 html 就是转化为 JS + Html Element 渲染,而 canvaskit 是采用 Skia + WebAssembly 的方式,而 html 的模式让 Web 在 Flutter 中显得「格格不入」,路径依赖和维护成本也一直是 Flutter Web 的头痛问题。
面对这个困境,官方在年初的 Flutter Forword 大会上提出重新规划 Flutter Web 的未来,而随着 Flutter 3.10 的发布,官方终于对于 Web 的未来有了明确的定位:
“Flutter 是第一个围绕 CanvasKit 和 WebAssembly 等新兴 Web 技术进行架构设计的框架。”
Flutter 团队表示,Flutter Web 的定位不是设计为通用 Web 的框架,类似的 Web 框架现在有很多,比如 Angular 和 React 等在这个领域表现就很出色,而 Flutter 应该是围绕 CanvasKit 和 WebAssembly 等新技术进行架构设计的框架。
所以 Flutter Web 未来的路线更多会是 CanvasKit ,也就是 WebAssembly + Skia ,同时在这个领域 Dart 也在持续深耕:从 Dart 3 开始,对于 Web 的支持将逐步演进为 WebAssembly 的 Dart native 的定位。
什么是 WebAssembly 的 dart native ?一直以来 Flutter 对于 WebAssembly 的支持都是:使用 Wasm 来处理CanvasKit 的 runtime,而 Dart 代码会被编译为 JS,而这对于 Dart 团队来时,其实是一个「妥协」的过渡期。
而随着官方与 WebAssembly 生态系统中的多个团队的深入合作,Dart 已经开始支持直接编译为原生的 wasm 代码,一个叫 WasmGC 的垃圾收集实现被引入标准,该扩展实现目前在基于 Chromium 的浏览器和 Firefox 浏览器中在趋向稳定。
目前在基准测试中,执行速度提高了 3 倍
要将 Dart 和 Flutter 编译成 Wasm,你需要一个支持 WasmGC 的浏览器,目前 Chromium V8 和 Firefox 团队的浏览器都在进行支持,比如 Chromium 下:
通过结构和数组类型为 WebAssembly 增加了对高级语言的有效支持,以 Wasm 为 target 的语言编译器能够与主机 VM 中的垃圾收集器集成。在 Chrome 中启用该功能意味着启用类型化函数引用,它会将函数引用存储在上述结构和数组中。
现在在 Flutter master 分支下就可以提前尝试 wasm 的支持,运行 flutter build web --help
如果出现下图所示场, 说明支持 wasm 编译。
之后执行 flutter build web --wasm
就可以编译一个带有 native dart wasm 的 web 包,命令执行后,会将产物输出到 build/web_wasm
目录下。
之后你可以使用 pub 上的 dhttpd
包在 build/web_wasm
目录下执行本地服务,然后在浏览器预览效果。
> cd build/web_wasm
> dhttpd
Server started on port 8080
目前需要版本 112 或更高版本的 Chromium 才能支持,同时需要启动对应的 Chrome 标识位:
enable-experimental-webassembly-stack-switching
enable-webassembly-garbage-collection
当然,目前阶段还存在一些限制,例如:
Dart Wasm 编译器利用了 JavaScript-Promise Integration (JSPI) 特性,Firefox 不支持 JSPI 提议,所以一旦 Dart 从 JSPI 迁移出来,Firefox 应启用适当的标志位才能运行。
另外还需要 JS-interop 支持,因为为了支持 Wasm,Dart 改变了它针对浏览器和 JavaScript 的 API 支持方式, 这种转变是为了防止把 dart:html
或 package:js
编译为 Wasm 的 Dart 代码,大多数特定于平台的包如 url_launcher 会使用这些库。
最后,目前 DevTools 还不支持 flutter run
去运行和调试 Wasm。
最后
很高兴能看到 Flutter 团队最终去定了 Web 的未来路线,这让 Web 的未来更加明朗,当然,正如前面所说的,Flutter 是第一个围绕 CanvasKit 和 WebAssembly 等新兴 Web 技术进行架构设计的框架。
所以 Flutter Web不是为了设计为通用 Web 的框架去 Angular 和 React 等竞争,它是让你在使用 Flutter 的时候,可以将能力很好地释放到 Web 领域,而 CanvasKit 带来的一致性更符合 Flutter Web 的定位,当然,解决加载时长问题会是任重道远的需求。