5.4 KiB
Executable File
自定义界面组件
使用 Hippy 开发过程中,当前的能力可能无法满足一些特定场景,这时候就需要对 UI 组件进行封装或者引入一些第三方的功能来完成需求。
组件扩展
扩展组件主要包括:
- 扩展
HippyView - 实现构造方法
- 实现设置自定义组件的
tagName - 实现构造自定义组件的
dom - 实现自定义组件的
API 能力 - 实现自定义组件的属性
其中 HippyView 类,实现了一些 HippyBaseView 的接口和属性定义,在一个自定义组件中有几个比较重要的属性:
- id: 每一个实例化 component 的唯一标识,默认会赋值给组件 dom 的 id 属性
- pId: 每一个实例化 component 的父组件标识
- tagName: 是用来区分组件的类型,也是业务侧代码使用该组件时在
nativeName属性上填写的值,用来关联自定义组件的 key - dom: 真正挂载到document上的节点
- props: 承载了从业务侧传递过来的属性和style的记录
例子
下面这个例子中,我们创建了 CustomView 的自定义组件,用来显示一个视频
- 第一步,进行初始化
import { HippyView, HippyWebEngine, HippyWebModule } from '@hippy/web-renderer';
// 继承自 `HippyView`
class CustomView extends HippyView {
// 实现构造方法
constructor(context, id, pId) {
super(context, id, pId);
// 设置自定义组件的 `tagName` 为 `CustomView`,
// 这样 JS 业务使用的时候就可以设置 `nativeName="CustomView"` 进行关联。
this.tagName = 'CustomView';
// 构造自定义组件的 dom,我们创建了一个 video 节点并赋值给 dom 成员变量。注意 dom 成员变量在构造方法结束前一定要设置上
this.dom = document.createElement('video');
}
}
-
第二步,实现自定义组件的 API 能力和相关属性
我们为
CustomView实现了一个属性src,当 JS 业务侧修改 src 属性时就会触发set src()方法并且获取到变更后的 value。我们还实现了组件的两个方法
play和pause,当 JS 业务侧使用callUIFunction(this.instance, 'play'/'pause', []);的时就会调用到这两个方法。在
pause()方法中,我们使用sendUiEvent向 JS 业务侧发送了一个onPause事件,属性上设置的回调就会被触发。
import { HippyView, HippyWebEngine, HippyWebModule } from '@hippy/web-renderer';
class CustomView extends HippyView {
set src(value) {
this.dom.src = value;
}
get src() {
return this.props['src'];
}
play() {
this.dom.play();
}
pause() {
this.dom.pause();
this.context.sendUiEvent(this.id, 'onPause', {});
}
}
关于
props:HippyWebRenderer底层默认会将业务侧传递过来的原始props存储到组件的props属性上,然后针对更新的prop项逐个调用与之对应 key 的 set 方法,让组件获得一个更新时机,从而执行一些行为。props里面有一个对象style,承载了所有业务侧设置的样式。默认也是由HippyWebRenderer设置到自定义组件的dom上,中间会有一层转换,因为style中的有一些值是hippy特有的,需要进行一次翻译才可以设置到dom的style上。
关于
context: 自定义组件被构造的时候会传入一个context,它提供了一些关键的方法:
export interface ComponentContext {
sendEvent: (type: string, params: any) => void; // 向业务侧发送全局事件
sendUiEvent: (id: number, type: string, params: any) => void; // 向某个组件实例发送事件
sendGestureEvent: (e: HippyTransferData.NativeGestureEvent) => void; // 发送手势事件
subscribe: (evt: string, callback: Function) => void; // 订阅某个事件
getModuleByName: (moduleName: string) => any; // 获取模块实例,通过模块名
}
复杂组件
有的时候我们可能需要提供一个容器,用来装载一些已有的组件。而这个容器有一些特殊的形态或者行为,比如需要自己管理子节点插入和移除,或者修改样式和拦截属性等。那么这个时候就需要使用一些复杂的组件实现方式。
- 关于组件自己实现子节点的 dom 插入和删除,
HippyWebRender默认的组件dom插入和删除,是使用 Web 的方法:
Node.insertBefore<T extends Node>(node: T, child: Node | null): T;
Node.removeChild<T extends Node>(child: T): T;
- 如果组件不希望以这种默认的形式来实现,可以自行通过
insertChild和removeChild方法管理节点的插入和移除逻辑。
class CustomView extends HippyView{
insertChild (child: HippyBaseView, childPosition: number) {
// ...
}
removeChild (child: HippyBaseView){
// ...
}
}
- 关于组件
props更新的拦截,需要实现组件的updateProps方法。
例子中,
data是组件本次更新的props数据信息,defaultProcess()是HippyWebRenderer默认处理props更新的方法,开发者可以在这里拦截修改更新的数据后,依然使用默认的props进行更新,也可以不用默认的方法自行进行属性更新的遍历操作。
class CustomView extends HippyView{
updateProps (data: UIProps, defaultProcess: (component: HippyBaseView, data: UIProps) => void) {
// ...
}
}