Hippy开发过程中可能有很多场景使用当前的能力是无法满足的,这就需要对UI组件封转或者引入一些三方的功能来完成需求。
接下来将以CustomView为例,从头介绍如何扩展组件。
扩展组件包括:
*扩展 HippyView
。
*实现 实现构造方法。
*实现 设置自定义组件的tagName
。
*实现 构造自定义组件的dom
。
*实现 自定义组件的api能力
。
*实现 自定义组件的属性。
HippyView类,实现了一些HippyBaseView的接口和属性定义。在一个自定义组件中有几个比较重要的属性:
-
id:每一个实例化component的唯一标识,默认会赋值给组件的dom的id属性
-
pId:每一个实例化component的父组件标识
-
tagName:是用来区分组件的类型,也是业务侧代码使用该组件时在nativeName属性上填写的值,用来关联自定义组件的key
-
dom:真正挂载到document上的节点
-
props:承载了从业务侧传递过来的属性和style的记录
下面这个例子中,我们创建了CustomView自定义组件,用来显示一个视频。
第一步:
继承自HippyView
。
实现构造方法。
设置自定义组件的tagName
为CustomView
,这样业务侧使用的时候就可以设置nativeName="CustomView"
进行关联。
构造自定义组件的dom,我们创建了一个video节点并赋值给dom成员变量。注意dom成员变量在构造方法结束前一定要设置上。
import { HippyView, HippyWebEngine, HippyWebModule } from '@hippy/web-renderer';
class CustomView extends HippyView {
constructor(context,id,pId) {
super(context,id,pId);
this.tagName = 'CustomView';
this.dom = document.createElement('video');
}
}
第二步:
实现自定义组件的api能力
实现自定义组件的属性
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',{});
}
}
上面这个例子中 :
我们为CustomView
实现了一个属性src
,当业务侧修改src属性当时候就会触发set src()
方法并且获取到变更后的value。
我们还实现了组件的两个方法play
和pause
,当业务侧使用 callUIFunction(this.instance, 'play'/'pause', []);
调用时就可以调用到这两个方法。
在pause()
方法中,我们使用sendUiEvent
向业务侧发送了一个onPause
事件,业务侧的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方法
class CustomView extends HippyView{
updateProps (data: UIProps, defaultProcess: (component: HippyBaseView, data: UIProps) => void){
...
}
}
上面的例子中,data
是组件本次更新的props
数据信息,defaultProcess()
是HippyWebRenderer
默认处理props
更新的方法,开发者可以在这里拦截修改更新的数据后,依然使用默认的props更新方法进行更新,也可以不用默认的方法自行进行属性更新的遍历和操作。