Weex 自定义 Component 开发这块,官方文档和网上示例都较少涉及。工作所需有所研究,总结此文以飨读者。

基础定义与注册

如下述代码所示,从 WXComponent 继承出来以后,复写四个构造器方法,就可以完成一个最简单可跑当然也显示不了任何东西的 WXComponet。

需要说明的是 WXComponent 可以指定泛型 T,T extends View,用于指定WXComponent hostView 根布局的类型。这个还是指定的比较好,在某些进阶用法中会需要这个类型。

public class DemoWXComponent extends WXComponent<T> {
    public DemoWXComponent(WXSDKInstance instance, WXVContainer parent,
                           int type,
                           BasicComponentData basicComponentData) {
        super(instance, parent, type, basicComponentData);
    }

    public DemoWXComponent(WXSDKInstance instance, WXVContainer parent, String instanceId, boolean isLazy,
                           BasicComponentData basicComponentData) {
        super(instance, parent, instanceId, isLazy, basicComponentData);
    }

    public DemoWXComponent(WXSDKInstance instance, WXVContainer parent, boolean isLazy,
                           BasicComponentData basicComponentData) {
        super(instance, parent, isLazy, basicComponentData);
    }

    public DemoWXComponent(WXSDKInstance instance, WXVContainer parent,
                           BasicComponentData basicComponentData) {
        super(instance, parent, basicComponentData);
    }
}

使用这个 Component 之前,还需要把他注册进 WXSDKEngine。如下所示:
一次注册后,在Android程序销毁之前,可以一直使用这个 Component。无需 unregister,WXSDKEngine 也没有提供 unregister 方法,这是显而易见的,因为当前还未产生任何实例。

WXSDKEngine.registerComponent("democomponent", DemoWXComponent.class);

值得一提的是 componet 名称,尽量不要下划线中划线和大写字母,否则可能会踩坑。

好,接下来是 Weex JS 代码怎么调用。无需 import,直接使用,只要 register 方法已经被执行过了,如下所示:

<democomponent />

如果想要设定 props 怎么办,如 <democomponent source=test/>。

那就在 Componet 中增加如下:

@WXComponentProp(name = "source")
public void setSource(String source) {
    mSource = source;
    // or do some things
}

weex 会根据外部传入的 props,根据注解调用对应 props 的 set 方法。

生命周期

显然,看了第一节,只能保证链路上 weex 自定义 Component 能跑起来,没有做其他任何事情。那么,为了能实现我们需要的渲染和其他逻辑,就需要了解 Weex Componet 的生命周期。这里的生命周期,实质就是了解可 Override 的几个 WXComponent 方法,和他们的被调用的时机。这一块官方没有任何文档,全靠去 github 源码中看和试。

必需 Override

initComponentHostView()

protected T initComponentHostView(@NonNull Context context) {
    // for example
    mComponentHostView = new FrameLayout(context);
    mComponentHostView.setId(R.id.fragment_content);
    return mComponentHostView;
}

用于生成根 View 返回给 Weex 来渲染。注意,不要在这里进行任何响应外界设入的 props 的渲染,因为此时极大可能 props 还没有被传入。

可选 Override

bindData()

@Override
public void bindData(WXComponent component) {
    super.bindData(component);
    // 这里进行 props 的响应渲染
}

super.bindData() 后即可响应 props 进行渲染,因为此时 props 的set方法都已经被调用过。

destroy()

@JSMethod
@Override
public void destroy() {
    super.destroy();
    // 进行自定义 Component 的必要销毁逻辑
}

如果有额外需要销毁的逻辑,需要写在 destroy 之中。weex 会在退出 WXActivity 或其他等同的时候调用。值得一提的是,我一般加一个 @JSMethod 注解,以提供前端 Weex 开发一个主动销毁的能力,避免需要的时候不能及时推代码生效,而要等到发版。

暴露方法

上段其实已经提到,怎样暴露一个 Component 方法给前端调用。如下所示:

@JSMethod
public void getDuration(JSCallback callback) {
    if (null != getCurrentShortVideoVh() && null != callback) {
        Map<String, Object> map = new HashMap<>(1);
        map.put("result", "value");
        callback.invoke(map);
    }
}

需要注意的是,直接把 void 改成返回值比如 boolean 然后试图 return 是没有用的,weex js 侧收不到。因此,必须要去使用回调来给返回值。如上所示。

DOM

Weex 新内核(WeexCore)将 Dom 层和 Layout 引擎下沉到 C++ 层实现,移除 Java 层的 DomObject,提升渲染性能和内核的可通用性。因此,github 最新版不再可以获取到 WXComponent 中的 DomObject。

Tricks:强转

如果发现自定义 Component 的逻辑需要用到 Activity,而 WXComponent 只给你提供了 Context 的时候,不要慌,Weex 传入的 Context 其实可以强转 Activity。当然,以防万一,记得用 instance of 保护一下。

同理,如果你想要弹出一个 Fragment,结果发现自己需要一个 FragmentActivity 来getSupportFragmentManager(),不要慌,weex 传入的这个 Activity 也可以强转为
FragmentActivity,同样记得加 instance of 保护,否则业务挂了不算我的,因为这毕竟是文档中的未定义行为。

03-05 20:16