在 iOS 接入 WeexCore 前,除了前端框架,iOS 与 Android 两个平台几乎没有共享的代码。从 Javascript 虚拟机开始,一直到最终绘制在屏幕上的系统 UI 组件,整个流程双端完全各自实现,除了 Layout 引擎。随着 Weex 不断演进,两端代码维护成本高,差异性大,进行跨平台融合势在必行。
与此同时 Weex 在进行渲染架构升级,将逻辑与界面分离,达到极致首屏渲染速度。新的架构直接在 WeexCore 上进行跨平台实现,并复用 WeexCore 的桥输出渲染指令。因此需要对 iOS 进行改造。
WeexCore 为了实现跨平台,使用 C++ 编写。其当前基本职责为:接收前端生成的渲染指令与对象数据,生成并维护 RenderPage (instance)、RenderObject(layout_node);向平台层输出经过处理的平坦化的组件信息和排版数据,由平台层创建 Native View 进行展示与交互。WeexCore 当前的职责还比较简单,为了提升平台一致性、简化平台层代码,未来会将更多的逻辑,如线程管理、JS 引擎接口等移植到WeexCore 层。最终目标是在平台层仅保留供业务使用的接口,以及 Native 组件的实现。
接入 WeexCore 前,iOS 已经使用了自主的 layout 引擎。其它逻辑均在 ObjC 层。Bundle 经过解析后,由 JSFramework 输出渲染指令和组件信息。通过 JavascriptCore 注册的方法传输给对应的 WeexInstance。每个 Instance 持有自己的 ComponentManager。由 ComponentManager 负责解析指令和组件,构建 Component Tree。此时 ComponentManager 拿到的指令还未平坦化,体现在:
此外,在接入 WeexCore 前,ObjC 层除了需要维护 Component Tree 外,还需要建立并维护排版引擎使用的 Layout Tree。架构如下:
接入 WeexCore 后,Layout 引擎下沉到 WeexCore 层,JSFramework 输出的渲染指令直接交给 WeexCore 中的 RenderManager。RenderManager 进行解析后,创建 RenderObject。RenderObject 直接是 LayoutNode 的子类,可由 LayoutEngine 处理。不需要像之前那样由上层 Component 创建并手工关联。RenderManager 将渲染指令平坦化,并输出相关 Action 到 Render Action Pool,再通过 PlatformBridge 传递给 ObjC 层的 ComponentManager。平坦化体现在:
此外,Layout 驱动主要由 WeexCore 进行。ObjC 层需要依赖 iOS 平台接口,实现与屏幕刷新同步的 Layout 检测。Layout 引擎生成新的坐标,也产生 LayoutAction,通过 PlatformBridge 传递到平台层;平台层的同步检测信号,也通过 PlatformBridge 传递回 WeexCore。
架构图如下:
初看两副架构图,当前的 WeexCore 还没有发挥出威力。未来,它将在 JSFramework 与平台层中间发挥越来越大的作用。
相比于接入前,拿到前端渲染指令和数据后,不做逻辑,直接将数据透传给 WeexCore。此处涉及一次 OC 对象到 wson 数据的转换,未来可以优化。
以 AddElement 为例,WeexCore 回吐的每条指令,只对应一个 Element。数据经过平坦化,但是需要转换回 OC 对象,未来可以优化。
Layout 逻辑修改是接入 WeexCore 最复杂的部分。
在原来的实现中,每个 WXComponent 被创建时,同时创建自己对应的 LayoutNode。平台层不但要维护 Component Tree,还要维护 Layout Tree。还要负责将 Styles 中 CSS 相关属性赋值给自己的 Node。在接入 WeexCore 后,每个 RenderObject 本身就是 LayoutNode。
为了支持 FixedComponent,在原来的实现中,额外引入了 RootLayoutNode,并将 Root Component 对应的 Node 添加为 RootLayoutNode 的子节点。在新的实现中,不再需要 RootLayoutNode,也不需要再创建任何额外的 LayoutNode。
在原来的实现中,对于 Scroller 类型的组件(Scroller、List、RecycleList等),为其创建了额外的 ScrollerLayoutNode。孩子组件都被添加到 ScrollerLayoutNode 中,本身的 LayoutNode 内容为空。当排版时,使用 LayoutNode 结果决定 Scroller 自己的位置,同时需要额外为 ScrollerLayoutNode 运行排版算法,决定所有孩子组件的位置。接入 WeexCore 后,不再需要这种 geek 的实现方式,同时大大减少了需要内部自排版的组件数目。
iOS 平台上 recycler/waterfall 组件依赖 UICollectionView 实现。对于 waterfall 组件,可以通过 auto 属性灵活定义列数、列宽、间隔等属性,实现自适应效果。接入 WeexCore 后,相关计算下沉到 WeexCore 中,上层不再关心,只需要按照排版结果展示组件即可。
Weex 支持 transition 动画,内部通过插值不断修改 layout 坐标实现动画效果,不受平台影响。因为接入 WeexCore 后 Styles 在底层被 WeexCore 分组,与 CSS 相关的布局信息不再上传到平台层。而 transition 动画起始值需要相关信息,故不能再从 Styles 中获取。与 Android 平台一致,从底层 RenderObject(也就是 LayoutNode)中获取。
Styles 不再上传到平台层后,一些三方自定义 WXComponent 不能再从 Objective-C 类中取到原始 CSS 样式信息,因此 SDK 提供了一组获取 CSS 样式的方法。
WeexCore 将成为两个平台提升开发效率,提升运行效率,提升一致性的重要工具。诸多技术改造和新功能也在开发中,如: