写点什么

把 Flutter 扩展到微信小程序端的探索,androidstudio 汉化

用户头像
Android架构
关注
发布于: 2021 年 11 月 03 日

// wxml 部分


<Center>


<Text>{{x}}</Text>


</Center>


// js 部分


Component({


data: {


x: 'Hello World'


}


})


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


我们需要根据 Flutter 代码生成相关小程序 wxml 模版文件 收集 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>


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


<template name="template004">


<Center>


<template is="{{templateName}}" data="{{...templateData}}"/>


</Center>


<template name="template003">


3、在运行阶段,会根据 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 结构,这个结构就包含了上文所说的 templateName, templateData,UI 描述将会被下层小程序获得,用来渲染小程序 UI,架构图如下:



Dart/JS:转化与互操作


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


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


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


方法等等,它们各自在本身的"域"里执行。


这带来两个问题:


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);

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
把Flutter扩展到微信小程序端的探索,androidstudio汉化