大厂Data+Agent 秘籍:腾讯/阿里/字节解析如何提升数据分析智能。 了解详情
写点什么

京东:将 Flutter 扩展到微信小程序端的探索

  • 2019-09-27
  • 本文字数:3341 字

    阅读完需:约 11 分钟

京东:将Flutter扩展到微信小程序端的探索

ARES 作为京东技术中台的多端融合技术团队,聚焦于跨端开发技术框架和平台搭建,包括但不限于 RN、Flutter、小程序等技术栈。目前已经广泛应用于京东商城、京东金融、京东到家、京东拼购等京东 La 系核心 APP 内,帮助业务团队低成本、快速开发自己的业务,以应对市场的瞬息万变之势。


Google Flutter 是一个非常优秀的跨端框架,不仅可以运行在 Android、 iOS 平台,而且可以支持 Web 和桌面应用。在国内小程序是非常重要的技术平台,我们也一直思考能否把 Flutter 扩展到小程序端?我们团队之前已经开源了 Alita 项目,Alita 可以把 React Native 的代码转换并运行在微信小程序平台。受此启发,我们认为同样是声明式 UI 框架的 Flutter 同样可以运行在小程序平台。


所以,我们发起了flutter_mp开源项目。以微信小程序为例,不过现阶段,flutter_mp 项目还处于早期的实验阶段,很多功能还在探索规划中,欢迎大家在 Github 上随时关注我们的最新进展,或者参与项目共同探索。

原理简介

虽然还有诸多功能未完成,我们先来谈谈整个 flutter_mp 的实现原理。篇幅原因,下面我们将只对 flutter_mp 几个重要的部分进行简单说明。


先看下 flutter_mp 的实际效果:



Flutter 版官方 layout 样例



通过 flutter_mp 转换并运行在小程序端效果

声明式 UI 的处理

Flutter 是声明式 UI 框架,声明式 UI 只需要向框架描述 UI 长什么样子而不用关心框架具体的实现细节,具体到 Flutter,上层的 UI 描述使用底层的 skia 图形引擎处理就是原生 Flutter,而把底层处理换成 html/css/canvas 就是 flutter_webflutter_mp 则是探索在类小程序上对这些 UI 描述的处理。


我们看一个最简单例子


var x = 'Hello World'
Center( child: Text(x));
复制代码


对于上面的 UI 结构,我们只需要在小程序的 wxml 文件里,用如下的结构对应就 OK 了。


// wxml部分<Center>   <Text>{{x}}</Text></Center>
// js 部分Component({ data: { x: 'Hello World' }})
复制代码


虽然实际的结构要比上面的情况复杂的多,不过通过上面简单的例子,我们知道起码要做两个事情:


  1. 我们需要根据 Flutter 代码生成相关小程序 wxml 模版文件

  2. 收集 wxml 渲染需要的数据,放置到小程序组件的 data 字段。

wxml 结构生成

我们知道小程序是无法动态操作节点的,wxml 结构需要预先生成,所以 Flutter 运行在小程序之前,会存在一个编译打包阶段,这个阶段会遍历 Dart 代码,根据一定规则生成 wxml 文件(编译阶段还会做下文将要提到的另外一个重要事情 — 把 Dart 编译为 js)。


具体的,我们首先会将 Dart 源码处理为可分析的 AST 结构,AST 是源代码的树型表示结构。然后我们深度遍历这份 AST 语法树结构,生成目标 wxml,整个过程如下:



构建 wxml 结构的难点在于: Flutter 不仅是声明式 UI 还是“值 UI”,什么叫“值 UI”?简单来说,Flutter 把 UI 看成是一个普通的值,类似于字符串,数字一样的值,既然是一个普通的值,就可以参与所有的控制流程,可以是函数的返回值也可以是函数参数等等。而小程序的 wxml 虽然也是声明式 UI,却不是“值 UI”,wxml 更加像模版,更加的静态。怎么用静态的 wxml 表达动态的“值 UI”是构建 wxml 结构的关键所在。


看个例子:


Widget getX() {    if (condition1) {        return Text('Hello');    } else if (condition2) {        return Container(            child: ...        );    } else if (condition3) {        return Center(            child: ...        );    }    ...}
Widget x = getX();
Center( child: x // < --- 如何处理这里的 x??);
复制代码


这里的 child: x ,x 是一个动态值,它的具体值需要在运行阶段才能确定,它可能是任意的 Widget,如何在静态的 wxml 上处理这里动态的 x?受 Alita 框架的启发,这里主要是借助于小程序 template 的动态性(template 的 is 属性可以接受变量值)。有如下几步:


  1. 首先在遍历 Dart 源码 AST 结构的时候,会把每一个独立完整的“UI 值”片段,对应到 wxml 的 template, 比如上文 getX 里面的 UI


<template name="template001">    <text>Hello</text></template><template name="template002">    <Container>...</Container></template><template name="template003">    <Center>...</Center></template>
复制代码


  1. 在遇到 类似 x 这种动态值的时候,固定地会生成一个 template 占位。


<template name="template004">    <Center>        <template is="{{templateName}}" data="{{...templateData}}"/>    </Center><template name="template003">
复制代码


  1. 在运行阶段,会根据 getX 函数的运行结果来决定 x 映射的“UI 值”,如果 getX 里面 condition1 为 true,那么这里的 templateName 的值就是 template001*。*具体的数据计算收集工作,参考下面要的 “渲染数据收集”过程。


可以看出 flutter_mp 处理“值 UI”方式,完全参考了 Alita。

渲染数据收集

wxml 结构的生成是在编译阶段就完成了,与它不同渲染数据是运行时的信息,随时会根据 setState 而改变。那么我们怎么收集出我们需要的渲染数据呢?


如果我们还是顺着 Flutter 的架构图,很难插入我们收集的钩子函数,另外 Flutter 的这个架构对于小程序来说太重了,下图红框里的这些过程对于小程序的渲染来说并不必要。最后由于最终的代码会被转化为 js,而 Flutter 本身依赖的库里面很多是不支持转化 js 的,比如 dart:ui 等等。



所以我们实现了一个极简极简的 Flutter 小程序版本 mini_flutter,在编译期我们会把所有对 Flutter 库的引用替换为 mini_flutter, mini_flutter 只存在到上图的 Rendering 阶段,这个 Rendering 的实现也是为小程序定制的, 在运行时期 Rendering 不断收集 Widgets 的信息。最终生成一个 UI 描述的 JSON 结构,这个结构就包含了上文所说的 templateNametemplateData,UI 描述将会被下层小程序获得,用来渲染小程序 UI,架构图如下:


Dart/JS:转化与互操作

Flutter 的开发语言是 Dart,而小程序的运行环境是浏览器,所以我们还需要把 Dart 编译为 JavaScript 代码。


在上文的编译打包阶段也提到这一点,这个过程主要是使用了 Dart 提供的 dart2js 工具,不过,针对小程序环境,生成的 js 代码仍需要做一些适配,另外虽然都是 JS 代码,dart2js 生成的 js 和小程序原生 js 的运行环境却是隔离的,也就是说它们是不能共享变量,方法等等,它们各自在本身的"域"里执行。


这带来两个问题:


  1. Widget 初始化 或者 setState 更新,生成的 UI 描述 JSON,如何传递给小程序"域"呢?

  2. 相关渲染回调,事件的都发生在小程序"域",这些信息如何传递给 Dart?


总结一下:Dart(最终会编译为 JS)与小程序原生 JS 如何互操作?


解决这个问题主要是借助 dart:js, package:js 这两个库:


Dart 操作 JS:


import 'package:js/js.dart';
@JS("JSON.stringify")external stringify(String str);
复制代码


这样当 Dart 代码调用 stringify 方法的时候,实际上会执行window.JSON.stringify方法


JS 操作 Dart:


// dart注册void main() {    context['dartHi'] = () {        print('dart hi!');    };}
复制代码


// js 调用window.dartHi()
复制代码


这里只是简单说明 Dart 与 JS 的互操作,另外由于小程序的运行环境是阉割以后的浏览器环境,flutter_mp 的实现还稍有不同。


总之,Dart 与 JS 是可以互操作的,这样就打通了上层 Flutter 环境和下层小程序环境。

布局系统

Flutter 的布局系统不同与 css,但是和 css 颇相似。



在上文提到的 Rendering 阶段,会根据 Widget 的布局属性,类别,约束条件生成一个等效的 css 样式。注意这里边界约束是上下文相关的。比如一个没有宽高的 Container 实际大小,不仅和子元素相关,还和父元素传递过来的边界约束条件相关,这个其实是比较麻烦的,能不能把 Flutter 的 Widget 属性,边界约束完全用 css 表达,我们还在寻求有效的方案。

总结

flutter_web 一样,完全把 Flutter 所有特性渲染到小程序上是不可能的,一般我们觉得应该是部分页面,部分功能需要运行在小程序上,这样使用 flutter_mp 才是有意义的。


正如前文所说,flutter_mp 还在很早期的阶段,社区的支持和反馈对我们来说特别宝贵。同时欢迎广大开发者一起来维护flutter_mp


如果你需要在生产环境实现小程序跨端开发,推荐使用我们成熟的 RN 转小程序项目Alita


2019-09-27 14:115219

评论

发布
暂无评论
发现更多内容

和声是容介态——为《链政经济:区块链如何服务新时代治国理政》一书作序

CECBC

千万级数据迁移与分表的技术方案-企业产品实战

谙忆

Java 后端 分库分表 引航计划

网络攻防学习笔记 Day144

穿过生命散发芬芳

高可用 9月日更

玩转TypeScript工具类型(下)

有道技术团队

typescript 大前端 网易有道

我愿意招什么样的产品经理?

石云升

产品经理 招聘 9月日更

汽车之家基于dubbo-go云平台的探索和实践

apache/dubbo-go

dubbo dubbo-go dubbogo Dubbo3

堡垒机作用之事后审计详细讲解-行云管家

行云管家

运维 网络安全 运维审计 事后审计

深入理解掌握零拷贝技术

Linux服务器开发

网络协议 零拷贝 Linux服务器开发 Linux内核 用户态

银行数字化转型指南:《区域性银行数字化转型白皮书》完整版重磅发布

百度开发者中心

最佳实践 银行数字化转型

dubbo-go github action 集成测试

apache/dubbo-go

dubbo-go Apache Dubbo Dubbo3

Mp3文件结构全解析(一)

轻口味

android 音视频 9月日更

JavaScript进阶(七)call, apply, bind

Augus

JavaScript 9月日更

直播预告|如何节省30%人工成本,缩短80%商标办理周期?

京东科技开发者

商标 企业服务 灵活用工

与顶级互联网公司技术大佬面对面聊聊RocketMQ

阿里巴巴云原生

阿里云 RocketMQ 云原生

区块链军事应用探析

CECBC

【初恋系列】那年的雨还在下...

人工智能~~~

幻读是啥,会有什么问题?如何解决?

Java MySQL 数据库 面试 后端

二十不惑的年纪,我简直走了狗屎运(4面拿字节跳动offer)

Java 程序员 架构 面试 计算机

Go 语言网络库 getty 的那些事

apache/dubbo-go

dubbo Go 语言 Dubbo3

服务实体经济,银行区块链应用正在画一个更大的圆

CECBC

作为一线技术人员,如何更好地提升自己

谙忆

管理 成长 引航计划

基于Tensorflow + Opencv 实现CNN自定义图像分类

华为云开发者联盟

tensorflow KNN OpenCV CNN

低代码应用:软件开发的一体化最新形态!

优秀

低代码

Alibaba内部“Java架构核心宝典”来袭,全新技术限时开源

Java 编程 程序员 架构 面试

2021年公有云市场的5大趋势

云计算

金融级分布式事务解决方案DTC

tom

通俗易懂 即时通讯初学者入门 WhatsApp技术架构

OpenIM

【音视频专题】音频质量评估方法那些事

声网

算法 音视频

iOS开发面试拿offer攻略之数据结构与算法篇附加安全加密

iOSer

ios 数据加密 iOS面试 iOS逆向 iOS算法

国庆高质量出行,可视化开启智慧旅游

ThingJS数字孪生引擎

大前端 物联网 可视化 旅游 数字孪生

Tapdata 实时数据中台在智慧教育中的实践

tapdata

京东:将Flutter扩展到微信小程序端的探索_大前端_严康_InfoQ精选文章