elementui验证框架-基于JS的高性能Flutter动态框架MXFlutter

2023-08-26 0 7,242 百度已收录

简介:2018年10月,手Q看店团队尝试使用Flutter作为iOS开发。 他们一接触Flutter,立刻就觉得Flutter虽然强大,但它不能像RN那样动态,这是阻碍我们使用她的唯一障碍。 。 看Google团队的动态计划,短期内应该不会推出,所以我就自己开始了这个技术探索项目。

基于JS的高性能Flutter动态框架

可能是目前为止放下的相对最完整的Flutter动态解决方案

介绍

项目代号:MXFlutter(Matrix Flutter)

核心思想是将Flutter渲染逻辑中的三棵树中的第一棵放入JavaScript中生成。 Flutter控制层封装完全用JavaScript实现。 您可以使用JavaScript来开发Flutter应用程序,开发方式与Dart非常相似。 使用轻量级Flutter Runtime的JavaScript版本生成UI描述并将其传递给Dart层的UI引擎。 UI引擎 描述UI以产生真正的Flutter控件。 因此,它在iOS上是完全动态的,完整的代码在github中。 如果对您有帮助,请给MXFlutter一个Star,给我们继续更新的动力^_*,github TGIF-iMatrix MXFlutter

在继续之前,我们先看一下整体结构,一句话介绍一下MXFlutter,就是用JavaScript按照Flutter的方式来开发Flutter。 汗...还是有点混乱,我们看一下下面贴出的代码。

影响

以下截图是在MXFlutter框架下用JS开发的。 你可以下载里面的源码,里面有完整的JS代码示例:

这是APP的示例截图

下面是UI截图对应的JS代码。 是的,你没有眼花缭乱。 这是真正的 JavaScript 代码,可以在 MXFlutter 运行时库上渲染 Flutter UI

<pre class="code-snippet__js" data-lang="javascript">

class JSPestoPage extends MXJSWidget { constructor() { super("JSPestoPage");    this.recipes = recipeList; }
build(context) { let statusBarHeight = 24; let mq = MediaQuery.of(context); if (mq) { statusBarHeight = mq.padding.top }
let w = new Scaffold({ appBar: new AppBar({ title: new Text("Pesto Demo") }), floatingActionButton: new FloatingActionButton({ child: new Icon(new IconData(0xe3c9)), onPressed: this.createCallbackID(function () {

}), }), body: new CustomScrollView({ semanticChildCount: this.recipes.length, slivers: [ //this.buildAppBar(context, statusBarHeight), this.buildBody(context, statusBarHeight), ], }), //body:this.buildItems()[0] });

return w; }

buildAppBar(context, statusBarHeight) { return SliverAppBar({ pinned: true, expandedHeight: _kAppBarHeight, actions: [ IconButton({ icon: new Icon(new IconData(1)), tooltip: 'Search', onPressed: this.createCallbackID(function () {

}), }), ], flexibleSpace: LayoutBuilder({ builder: function (context, constraints) { size = constraints.biggest; appBarHeight = size.height - statusBarHeight; t = (appBarHeight - kToolbarHeight) / (_kAppBarHeight - kToolbarHeight); extraPadding = new Tween({ begin: 10.0, end: 24.0 }).transform(t); logoHeight = appBarHeight - 1.5 * extraPadding; return Padding({ padding: EdgeInsets.only({ top: statusBarHeight + 0.5 * extraPadding, bottom: extraPadding, }), child: Center({ child: new Icon(new IconData(1)) }), }); }, }), }); }

buildBody(context, statusBarHeight) {

let mediaPadding = EdgeInsets.all(0); let mq = MediaQuery.of(context); if (mq) { mediaPadding = MediaQuery.of(context).padding; } let padding = EdgeInsets.only({ top: 8.0, left: 8.0 + mediaPadding.left, right: 8.0 + mediaPadding.right, bottom: 8.0 });

return new SliverPadding({ padding: padding, sliver: new SliverGrid({ gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent({ maxCrossAxisExtent: _kRecipePageMaxWidth, crossAxisSpacing: 8.0, mainAxisSpacing: 8.0, }), delegate: new SliverChildBuilderDelegate( function (context, index) { let recipe = this.recipes[index]; let w = new RecipeCard({ recipe: recipe, onTap: function () { showRecipePage(context, recipe); }, });

return w; }, { childCount: this.recipes.length, }), }), }); }

(向左滑动查看完整代码,下同)

源码中有更丰满的例子,高仿知乎页面JSFlutter版本

这是对应的UI,已经接近于在线版中直接使用了。

这个漂亮的知乎页面是用Dart版本的JS转换而来的。 感谢作者徐继友。 你可以关注他。

现状

尽管MXFlutter的各个模块都比较完整,但在投入生产时仍然需要解决其中的Bug。 由于团队在2019年初启动了一个新项目,所以非常忙碌,几乎没有时间继续开发。 自3月份以来,该项目已暂停。 目前,人手非常紧张。 如果你有兴趣的话elementui验证框架,我期待和你一起丰富MXFlutter的动态能力。

0x00 分享动态探索过程中的几个炮灰解决方案

Flutter动态方案一:静态解析Dart语言并生成UI描述

Dart 本身是一种描述语言。 IDE的Outline工具可以解析Dart代码以生成树结构。 我们可以使用它的源代码来生成 JSON UI 描述。 相关代码:

dart-sdk:分析服务器

Dart静态分析的缺点是不能写逻辑,对UI代码的编译有很多限制。 不会写判断语句和函数。 支持这一点的成本非常高。 所以我不得不放弃。

快速介绍Flutter核心渲染模块的三棵树

响应式 UI 框架

WidgetTree:Widget存储了一个视图的配置信息elementui验证框架,可以高效地创建(构建)和销毁

Element是一个中间层,将WidgetTree与真正的渲染对象分开。 WidgetTree用于描述对应的Element属性

RenderObject 执行 Diff、Hit Test 布局、绘图

第一棵树有完整的UI描述信息,所以我只需要在JIT下通过DartVM创建第一棵树,随着时间的推移其他操作都扔到AOT中。

Flutter动态方案二:动态运行Dart语言并产生UI描述

与第一种静态分析Dart相比,第二种方案是编写一个非常轻量级的运行时库,让编译UI的Dart代码运行起来,生成树形结构,然后序列化为JSON(调试)、FlatBuffers(发布) UI描述.动态解析方案

具体渲染逻辑

整体结构

还有一个框架和解决方案。 运行它还有一些麻烦的事情要做。 需要移除 DartVM、Dart JIT 层的轻量级运行时库以及将 DSL 转换为 Dart AOT 层真正的 Widget 的 UIEngine。 哦,就是图中蓝色和黄色的三个部分

提取 DartVM

不能简单地改变编译条件来提取

Dart源码在编译时,会通过DART_PRECOMPILED_RUNTIME宏进行条件编译,所以在Debug版本中编译的是JIT模式,在Release版本中编译的是AOT模式。 并且这两种模式是互斥的,不能同时存在。

简单的解决方案是

我们单独编译一个DartVM,打包成动态库,并修改导入符号以避免合规性冲突

引入 DartVM 仍需开展工作

暂时未解决的问题

安装包太大,DartVM将安装包缩小了30M。 如果加上原来的AOT40M,整个Flutter安装包就会缩小到70M,这对于使用DartVM来说是不现实的。 怎么做。

0x01 最终方案JavasSriptCore替代DartVM

可能性剖析

JavasSriptCore是iOS官方库,不减安装包

Dart代码与JS代码非常相似,可以使用工具进行转换

JavasScriptCore和Native有更方便的互调套接字

ReactNative已经验证通过JS开发App能力是可行的

JS执行效率是DartVM的3倍,编码1M JSON仅需2毫秒

需要解决的问题

用 JS 开发一个假的 Flutter Runtime

封装JavasScriptCore和Native、Flutter互调socket

0x02 讲解MXFlutter的渲染原理

渲染树

两个重要的数据结构

MXScriptWidget管理一个Script页面或控件,负责创建和管理ScriptWidgetTree,通过自增ID与Flutter对应的Widget互相调用,每次Build时都会创建一个新的MXWidgetTree

MXFlutter 事件

在JS端构建Widget时,我们会为函数事件生成一个唯一的自增callbackID,并与widgetID组合起来形成widgetID/callbackID,作为storm的唯一标识。 当用户点击界面上的按钮时,事件从Flutter端传输到JS端,通过解析widgetID/callbackID找到widget对应的回调,完成风暴处理。

MXFlutter高效动态列表

在JS端,当ListView调用Build方法时,会提前展开child,并将children成员变量添加到ListView中。 这时,因为只有数据配置,不会有多余的Layout过程,所以速度很快。

preBuild(jsWidget, buildContext) { if(this.builder) { for (let i = 0; i < this.childCount; ++i) { let w = this.builder(buildContext, i); this.children.push(w); } delete this.builder; }

super.preBuild(jsWidget, buildContext);}

Flutter 这边,ListView 仍然是动态创建的,滑动列表,MXFlutter Engine 根据 Children 数组中的配置数据创建一个真正的 Flutter WidgetCell,效率和原生一模一样。

ListView.builder( itemCount: children.length, itemBuilder: (context, index) { return UIEngine.toWidget(children[index]); },)

MXFlutter动画方案

动画参数在VM层配置一次,动画开始后在Flutter层闭环重建,形成动画效果。 这是一种比较常见的做法。

0x03 渲染优化

无论 JSWidget 创建得有多快,总会存在跨语言执行的情况,因此减少 Build 次数并减小 Build 后 DSL UI 描述的大小可以优化性能。

渲染优化1-部分刷新:配置树差异

事实

无论如何,自动比较两个 Widget 并不会直接创建新的 Widget。 如果开发者不参与,框架自动估计Diff就得不偿失了

可行的办法

以牺牲响应式 UI 框架为代价的设计模式

Native和Web的形式,开发者自己参与设置Diff节点,即根据ID获取对应的Widget,修改Widget参数,重新构建生成新的DSL

渲染优化2-部分刷新-嵌套节点

渲染优化3-动静态控制可分离

MXStatelessWidget 可以通过使用无状态 ScriptWidget 被框架识别。 它下面的子树在每次构建中都不会改变。 构建结果会被缓存,下次直接在Flutter层复用。

内存-跨层镜像对象的生命周期

VM层、Flutter层、Native层如何控制镜像对象的生命周期?

参考Apple的iOS JavaScriptCore和Objective-C解决方案

重点关注Flutter层的对象生命周期

在不减少对象引用计数的情况下减少VM层的WeakMap支持。 Flutter层释放后,释放VM层对象

在Native层使用JSManagerValue,VM层对象释放后,手动清空Native引用

线程问题

参考业界RN等框架的设计,VM层运行在单独的后台线程中

Flutter层通过Native通道调用VM,发生两次线程切换

Flutter UI层和MXScript层是异步调用,限制了动态控件的架构设计

一个可能的解决方案

修改FlutterEngine,定制开发Dart->Native->VM通道,调用VM无需切换线程

VM不会创建新的线程,直接由Flutter UI Thread消息循环驱动,也支持与Flutter UI层的高效同步调用,但需要注意的是,从Native调用VM需要自定义FlutterEngine套接字。

0x04 让开发者写出高贵的代码

让开发者写出高贵的代码,咳咳,这有点吹了,总之我们想让使用MXFlutter的开发者写的代码看起来更正式、更好看。

参考JS示例源码

TGIF-iMatrix home_page.js

0x05 MXFlutter基础设施建设

由于JavaScript不支持模块化开发,无法引用其他文件代码,因此我们参考RN,使用Node.js模块化代码,并在Native层支持require语法。 开发时IDE最好选择VSCode,因为可以安装JS插件,直接运行调试JS

另外,我们将模拟器的JS路径文件重定向到开发机,用户更改JS文件后可以直接看到相应的变化,实现模拟器页面的热更新。

结语

由于时间限制,MXFlutter 还存在很多遗留问题。 作为一种技术探索,非常辛苦,但又非常有趣。 期待各位专家的指导,也期待朋友们提出的问题一起讨论解决。

要了解一切,您必须下载源代码,运行它并查看。 如果有什么疑问,可以留言讨论。 MXFlutter将持续更新。

其他项目成员包括 Luca Lang、nice 和 yockie,他们贡献了动画、控件和示例 APP 等核心实现。 Chaodong先生负责DartVM解决方案,IP先生帮助提供单元测试,健身专家Yofer先生负责代码维护和工具构建。 。

TGIF-iMatrix是一支技术氛围浓厚的团队,有帅哥有美女,有趣有爱,还有精通量子估计、5G等前沿技术的数据分析胜利者老王。 欢迎iOS、Android开发伙伴、数据开发、数据分析职位的朋友联系我发简历

对MXFlutter感兴趣的朋友可以进群交流

群号:747535761

另外,为我们的项目做一个小广告。 大家点点看一些视频——年轻人爱看的腾讯短视频。看视频

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 elementui elementui验证框架-基于JS的高性能Flutter动态框架MXFlutter https://www.wkzy.net/game/158455.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务