# 简介

BindingX 是解决 weexReact Native 上富交互问题的一种解决方案。
它提供了一种称之为 "Expression Binding" 的机制可以在 weexReact Native 上让手势等复杂交互操作以60fps的帧率流畅执行,
而不会导致卡顿,因而带来了更优秀的用户体验。

# 背景

由于 weex 底层使用的 JS-Native Bridge 具有天然的异步特性,这使得 JSNative 之间的通信会有固定的性能损耗,因此在一些复杂的交互场景中,JS 代码很难以高帧率运行。举个例子,如果我们要实现 “视图随手势移动” 的效果,那么按照传统的方式,需要在这个视图上绑定 touch 或者 pan 事件,当手势发生时, Native 会将手势事件通过 Bridge 传递给 JS , 这产生了一次 NativeJS 的通信。而 JS 在接收到事件后,需要根据手指移动的偏移量驱动界面变化,这又会产生一次 JSNative 的通信。与此同时,手势回调事件触发的频率是非常高的,频繁的的通信带来的时间成本很可能导致界面无法在16ms中完成绘制,进而产生卡顿。
事实上,不仅仅是在 weex 上存在这种问题, React Native 等框架同样存在类似的问题。拿 React Native Animated (opens new window) 组件为例,为了实现流畅的动画效果,这个组件采用了声明式的API,在 JS 端仅仅定义了输入与输出以及具体的 transform 行为,而真正的动画是通过 Native DriverNative 层执行,这样就避免了频繁的通信。然而,这个方案只能解决一部分问题,如果是有复杂交互操作的场景就不够用了。另外,声明式的方式能够定义的行为非常有限,无法满足更复杂的交互场景。

# 原理介绍

我们通过探索,提出了一种全新的方式用来解决这个问题,方案称之为 Expression Binding
还是拿 “视图随手势移动” 这个场景举例。我们的方案是这样的:
在手势开始前, JS 将具体的手势控制行为以 “表达式” 的方式传递给 Native ,我们定义了两个变量 xy ,分别代表手势过程中横向和纵向的偏移量。 那么”跟手移动”的表达式应该是这样的:

(伪代码)

f(x) = x
f(y) = y

另外,我们最终希望改变的是 “某视图”的 translateXtranslateY 属性,那么我们同时再把”期望改变的属性”和”某视图”的引用(anchor)也传递给 Native ,像这样:

(伪代码)

{
   
   anchor: foo_view.ref                    // ----> 这是"产生手势的视图"的引用  
   props:
            [
                {
                    element: foo_view.ref, // ----> 这是"期望改变的视图"的引用
                    expression: f(x) = x,  // ----> 这是具体的表达式
                    property: translateX   // ----> 这是期望改变的属性
                },
                {
                    element: foo_view.ref,
                    expression: f(y) = y,  // ----> y 属性
                    property: translateY
                }
            ]
}

您可能注意到在上面这段伪代码中,”视图的引用”(foo_view.ref)被传递了两次,这是因为 “产生手势的视图” 和 “期望改变的视图” 并不总是同一个。
比如说,你希望在视图A上滑动,而改变的是视图B的位置。因此,我们进行了区分,这样会更加通用。另外,正如上面说的,我们通过一个数据集合(即 element 、 expression 、 property )来描述 “视图” 的行为。因为手势可能会伴随着视图多个属性的变换,所以props中可以传递一个数组。 当这份配置传递到 Native 后, Native 会对目标视图( anchor )设置手势事件监听器。当手指在屏幕上移动时,监听器会收到回调事件。紧接着, Native 会直接根据其内置的 表达式解析引擎 去解释执行表达式,并根据表达式执行的结果驱动视图变换。比如手势横向滑动 60px 纵向滑动 70px ,那么表达式 f(x)=x 和 f(y)=y 的结果分别是 60 和 70,再根据对应的属性 translateX 和 translateY 就可以对视图进行平移了。 整个过程不再需要和 JS 端进行通信,因此用户体验如丝般顺滑。
以上就是这套方案的基本原理。一句话总结:

BindingX 的核心思想就是将”交互行为”以表达式的方式描述,并提前预置到Native,避免在行为触发时JS与native的频繁通信。

事实上, BindingX 解决的不仅仅是手势交互问题,理论上任何 “频繁通信+UI更新” 的场景都可以使用这套方案。 BindingX 目前支持已经以下四种场景:

  • 监听 pan 手势,更新UI。
  • 监听滚动容器(如List)的onscroll事件,更新UI。
  • 监听设备传感器方向变化,更新UI。
  • 动画。(即监听设备的每一帧的屏幕刷新回调事件,更新UI)。

# 平台支持

BindingX 通过插件的形式同时支持 React Nativeweex。在 weex 上,可以作为一个 weex 模块 直接注册到weex环境中, 在 JS 层同时支持 weex DSLRax DSL 。在 React Native 上,也可以以类似的方式使用。