首先感谢@乱冰@祖建国对FFW预研的投入!背景谷歌在最近的GoogleI/O上??推出了FlutterforWeb,旨在进一步解决代码一次多终端运行的问题。FlutterforWeb仍处于早期试用版,官方不推荐在生产环境中使用。那么它的实际情况如何呢?我们做了一个预研。希望本次预研的结果能帮助大家决定是否使用FFW。FlutterforWeb原理FlutterforWeb和Flutter都是上层的Dart环境。两者的区别在于Flutter的Dart代码运行在Dart虚拟机中,界面由Flutter引擎处理,通过Skia绘图引擎通过GPU绘制到屏幕上。FlutterforWeb的Dart代码被编译成JavaScript,界面部分转换成标准的html标签,部分转换成通过Canvas绘制的自定义标签,最后形成dom树。这个原理上的区别非常重要,直接让我们通过原理得出如下结论:FlutterforWeb在一致性和体验上存在矛盾。如果FlutterforWeb追求(和Flutter)完美一致,必然需要大量使用Canvas来绘制,而Canvas绘制组件(尤其是移动端)的性能至少不比html标签好。如果FFW追求性能极限,大量使用标准的html标签,这会带来和Weex、RN等一样的一致性问题:对于Flutter的所有控件,在绘图引擎上绘制一套代码,对于Flutter对于Webif要使用大量的html标签,如何保证一致性?只是很多精细的打磨工作。所以FFW必须处理好这个平衡。为什么用canvas画性能不如手写html?多角度定性分析:画在canvas上的FFW组件有很多MD特有的视觉效果和动画,比如阴影、Z轴变化等,对性能的消耗比普通的html标签要大。FFW是由Dart的DSL转换而来的dom树结构。转换后的dom树很复杂,不可能比手写的dom树更简洁。使用画布控件,手势事件的捕获和分发是困难的。它是由FFW框架本身实现的。虽然emmmm不排除谷歌发力创造奇迹的情况,但是无论如何,同等素质,同样界面的开发者,不可能比html+css+js表现的更好。还有一点,如果FFW原则上涉及到大量HTML标签的转换,必然涉及到分片处理。浏览器的碎片化程度不亚于安卓系统。Flutter本身之所以受到这么多人的重视,是因为它完美地避免了碎片化,通过图形引擎层保证了一致性。所以最好的平衡是标准html标签中只有有限的一部分可以被FFW复用,必须具备几个属性:标签本身的功能简单直观,最好不要有直接的图形显示,或者只有负责简单图形显示(比如画正方形)的典型标签是
和
。Flutter官方就是这么干的,所以我的结论是:一致性一般没有问题,性能上,FFW应该不会比纯手写的html标签界面好。官方状态&建议根据官网和Githubrepo,我们整理了一下:FlutterforWeb和Flutter目前暂时是两个仓库,官方正在进行合并,还没有给出定论。这一点在工程上很重要,说明了几个问题:目前官方对FFW的成熟度没有信心,FFW的迭代速度也很快。目前FFW和Flutter顶多保证的是同一个API,实现原理可能大相径庭。同时,也不保证所有控制措施都已在FFW上实施。官方不建议在生产环境中应用。目前插件能力非常有限,缺少一些与系统交互的能力,比如拍照。性能无法保证,运行会很慢,可能会掉帧。FFW中桌面的UI部分没有完成(跟我无线有什么关系?)开发时只能在Chrome中调试(这有什么关系?),release版本是可以运行在任意浏览器(IE除外,支持的最低版本存疑)。在实践中,对于这样一个新东西,官网上的内容确实不多,这些问题简单来说似乎也没什么,至于能不能用,还是要进去预研抱着吃螃蟹的心态,为了早日搞定Qing,打算找一个我们app已经做好的flutter页面,迁移到FFW,评估一下整个迁移过程,再看看页面效果,基本得出结论。具体的迁移细节我就不说了。官网也有迁移文档。总的来说,安装FlutterforWeb工具webdev,更改SDK依赖,添加新的Web文件夹(与之前存在的android和ios文件夹同级)就这么几步,添加一些其他文件(index.html、main.dart等)。将所有flutter代码依赖的flutter包修改为flutter_web包,去除所有不兼容的代码,如多语言、路由、Platform.isAndroid等。编译运行实践主要目的如下:深度整体的坑和知识广度,方便计算填坑成本,对FFW的整体性能和体验有一个把握,尤其是在FFW上跑自己的页面是什么感觉。对FFW与JS的相互调用有具体的了解。如果可行,那么复用组现有能力(比如mtop)的坑就会小很多。在下面的总结中,体验部分是我对mix2s的感受:项目支持debug和release模式,后者比前者性能更高(区别很明显)。debug模式支持代码修改后自动重新编译,支持和其他前端框架一致的hotreload(我只用过Django)。我没试过支持webdevbuild命令编译index.html+js,可以用nginx做反向代理。非常重要编译后的代码,gzip压缩前,最简单的helloworld的main.dart.js大小约500k,样本中gallery大小约2M,阉割后的纯显示顺序列表大小版本约1.3M。gzip对文本的压缩率一般为80%,压缩后往往需要几百千字节的大小。而且,在加载了main.dart.js之后才会显示界面。开发经验很重要。Flutterforweb使用flutter_web库,不支持很多其他插件。这会带来几个工程上无法优雅解决的问题。flutter和flutter_web并存最多能解决。一个dart2conditionalimport(这个功能在官方文档中没有)有几个依赖fluttersdk的库,尤其是多语言库,不能应用到flutter_web上,Google肯定不会单独适配flutter_web,合并的时候会支持。这意味着现阶段所有依赖fluttersdk的库都不能被flutter_web使用。难调试错误日志可以打印到浏览器控制台,但是trycatch部分的栈不好。浏览器中的栈很复杂,但基本上都能找到错误的dart代码。目前没有单步调试能力Platform.isAndroid全部报错,对于Android和iOS的差异化展示还不知道有没有其他办法。目前控件的API没有不一致,但是有些控件表现的很不正常,比如下拉刷新,在安卓手机上经常卡顿失败。猜测可能是一些交互比较重的控件也有类似的问题,但是测试成本太高了。图像控件NetworkImage可以直接使用,但是现在有点乱。不知道是官方控制的问题还是我们cdnurl策略的问题。Dart代码可以调用js,开发体验类似反射,需要处理JS类型和Dart类型的转换。Dart和JS的交互速度未知。只能说,即使FFW有很多浏览器API不支持,它也可以通过JS来扩展自己的能力。Dart和Js语言本身的差异会增加测试和兼容性的成本。Dart虽然可以编译成Js,但是Dart运行在DartVM上的性能和编译成Js运行在浏览器中的性能并不完全一样。.例如,对于以下代码Mapquery=null;valb=query["abc"];代码可以在DartVM中执行,结果为b=null;但是编译成Js运行的时候,因为query是null,所以会报空指针。用户体验我分别在chrome、uc和小米内置浏览器上尝试了订单列表和官方示例图库界面。体验如下:加载速度差不多,因为是局域网环境,感觉不出区别。很重要的是,当只有main.dart.js和一些资源文件(比如MaterialIcons)没有加载时,界面是不会显示的。无论是帧率还是流畅度,chrome>uc>>小米自家浏览器,其中chrome的体验最流畅,但样例中的一些动画和页面过渡也能看到明显的卡顿。文字显示方面,看flutterforweb的界面,chrome可以把大部分文字处理的很清晰,而小米自带浏览器中文字明显模糊。对比下面两张图,点击放大查看,FFW页面文字明显模糊。所有浏览器都无法选择要复制和粘贴的文本。Flutterforweb应该使用canvas来处理和显示文本,以此来解释为什么有些字体大小模糊,无法选中文本。iOSsafari和chrome访问demo页面时,整个TextField不可用,包括以下问题:软键盘弹出逻辑怪异,大部分时间弹出自动缩回,小部分正常弹出的时间(Android部分正常运行);但当焦点转移时,它不会自动收回。Safari无法输入文字,而chrome可以输入(安卓部分正常)并且可以选择文字,复制粘贴不生效(安卓也有同样的问题)。当焦点在TextField上时,界面会自动放大,此时很难缩回(Android部分表现正常)总结根据以上,整体总结,FlutterforWeb有几个比较严重的问题,如果不解决,估计不能应用到生产环境:包大小问题,会带来几个问题:FFW包比普通的h5包大很多,对流量和页面都是很大的挑战加载速度。FFW被打包成一个JS包,多个FFW页面无法复用公共组件,进一步造成浪费。FFW的js包加载不全,页面无法显示,用户体验极差;而H5可以渐进式加载,js可以后期进入。SDK分离的问题也会带来几个问题:在工程上,很难优雅的解决两个SDK共存的问题。能力方面依赖FlutterSDK的官方库,比如多语言库,不支持FFWSDK。您只能自己开发一套多语言解决方案。不要将FFW用于表单场景。不知道上面提到的TextField问题能否在应用层解决。一些交互组件,比如pull-to-refresh,有问题,几乎不能用。不确定整体部件的质量,一个一个看太贵了。结论没有非常强烈的业务需求或技术推动力,不要尝试在生产环境中使用FlutterforWeb。如果你有填坑的决心,愿意投入,而且不在意包大小和帧率等用户体验,可以考虑在这个阶段尝试一下。我个人判断填孔的成本在100多man-days(上限未知),有些孔(封装尺寸)可能根本填不上。我什么时候可以再次跟进?我觉得当FFW合并到FlutterSDK中的时候,他们的具体方案,你需要去问Google的人。本节的主要目的是列出假设要做FFW我们需要做的技术事项和相应的解决方案。本文作者:肩读原文本文为云栖社区原创内容,未经许可不得转载。