# 0x1 为什么要接入 WeexCore

在 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 组件的实现。

# 0x2 iOS 接入前后架构

接入 WeexCore 前,iOS 已经使用了自主的 layout 引擎。其它逻辑均在 ObjC 层。Bundle 经过解析后,由 JSFramework 输出渲染指令和组件信息。通过 JavascriptCore 注册的方法传输给对应的 WeexInstance。每个 Instance 持有自己的 ComponentManager。由 ComponentManager 负责解析指令和组件,构建 Component Tree。此时 ComponentManager 拿到的指令还未平坦化,体现在:

  • 组件和它的孩子节点信息是在一条指令中输出的,需要 ComponentManager 进行递归解析。
  • Styles 数据里,CSS、自定义属性等融合在一起。

此外,在接入 WeexCore 前,ObjC 层除了需要维护 Component Tree 外,还需要建立并维护排版引擎使用的 Layout Tree。架构如下:

Screen Shot 2018-08-03 at 15.00.43.png

接入 WeexCore 后,Layout 引擎下沉到 WeexCore 层,JSFramework 输出的渲染指令直接交给 WeexCore 中的 RenderManager。RenderManager 进行解析后,创建 RenderObject。RenderObject 直接是 LayoutNode 的子类,可由 LayoutEngine 处理。不需要像之前那样由上层 Component 创建并手工关联。RenderManager 将渲染指令平坦化,并输出相关 Action 到 Render Action Pool,再通过 PlatformBridge 传递给 ObjC 层的 ComponentManager。平坦化体现在:

  • 组件及它的孩子被拆分成多条 AddElementAction 输出。
  • Styles 被归类,如 CSS Layout 相关属性直接设置给 RenderObject(其父类为 LayoutNode),不再上传给平台层。Margin、Padding、其它属性被归类后上传平台层。

此外,Layout 驱动主要由 WeexCore 进行。ObjC 层需要依赖 iOS 平台接口,实现与屏幕刷新同步的 Layout 检测。Layout 引擎生成新的坐标,也产生 LayoutAction,通过 PlatformBridge 传递到平台层;平台层的同步检测信号,也通过 PlatformBridge 传递回 WeexCore。

架构图如下:

Screen Shot 2018-08-03 at 16.54.25.png

初看两副架构图,当前的 WeexCore 还没有发挥出威力。未来,它将在 JSFramework 与平台层中间发挥越来越大的作用。

# 0x3 iOS 接入主要改造点

# 0x31 将前端渲染指令传给WeexCore

Screen Shot 2018-08-03 at 16.57.39.png

Screen Shot 2018-08-03 at 16.57.48.png

相比于接入前,拿到前端渲染指令和数据后,不做逻辑,直接将数据透传给 WeexCore。此处涉及一次 OC 对象到 wson 数据的转换,未来可以优化。

# 0x32 实现 PlatformBridge,接收平坦化后的指令

Screen Shot 2018-08-03 at 17.06.02.png

以 AddElement 为例,WeexCore 回吐的每条指令,只对应一个 Element。数据经过平坦化,但是需要转换回 OC 对象,未来可以优化。

# 0x33 Layout 逻辑修改

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 的实现方式,同时大大减少了需要内部自排版的组件数目。

# 0x34 其它修改点

  • 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 样式的方法

# 0x4 To be continued...

WeexCore 将成为两个平台提升开发效率,提升运行效率,提升一致性的重要工具。诸多技术改造和新功能也在开发中,如:

  • iOS/Android 共用的 JSEngine 将 DOM 指令直接输出给 WeexCore,iOS 不再对接系统 JSContext/JSValue,实现引擎层面的架构统一,同时做到 JSEngine 可替换。
  • Eagle 高性能虚拟机将 DOM 指令输出给 WeexCore,由 WeexCore 输出渲染指令,上层对虚拟机是 Javascript 虚拟机还是 Eagle 虚拟机无感知。
  • WeexCore 做到中间桥梁将渲染指令输出给 Native 组件或自绘组件,实现类似 Flutter 与浏览器的纯自绘页面,使 Weex 既可对接 iOS/Android 系统 UI 库,也可以对接任意自绘 UI 库。
上次更新: 2019-12-2
文档写得很棒文档写得很差