介紹
該方案展示了ArkWeb同層渲染:將系統(tǒng)原生組件直接渲染到前端H5頁面上刺彩,原生組件不僅可以提供H5組件無法實現(xiàn)的一些功能缰冤,還能提升用戶體驗的流暢度
效果圖預覽
使用說明
- 進入頁面即可看到同層渲染效果,Text,search鲤看,image都是原生組件。
實現(xiàn)思路
- 添加權限。
"ohos.permission.INTERNET"
- 創(chuàng)建控制器管理綁定的NodeContainer組件。
class SearchNodeController extends NodeController {
private rootNode: BuilderNode<[Params]> | undefined | null = null;
private embedId : string = "";
private surfaceId : string = "";
private renderType :NodeRenderType = NodeRenderType.RENDER_componentTypeDISPLAY;
private componentWidth : number = 0;
private componentHeight : number = 0;
private componentType : string = "";
setRenderOption(params : NodeControllerParams): void {
this.surfaceId = params.surfaceId;
this.renderType = params.renderType;
this.embedId = params.embedId;
this.componentWidth = params.width;
this.componentHeight = params.height;
this.componentType = params.type;
}
/**
* 在對應NodeContainer創(chuàng)建的時候調(diào)用巴碗、或者通過rebuild方法調(diào)用刷新
*/
makeNode(uiContext: UIContext): FrameNode | null{
this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId, type: this.renderType});
if (this.componentType === 'native/component') {
this.rootNode.build(wrapBuilder(searchBuilder), { width : this.componentWidth, height : this.componentHeight});
} else {
}
// 返回FrameNode節(jié)點
return this.rootNode.getFrameNode();
}
/**
* 設置BuilderNode節(jié)點
*/
setBuilderNode(rootNode: BuilderNode<Params[]> | null): void{
this.rootNode = rootNode;
}
/**
* 獲取BuilderNode節(jié)點
*/
getBuilderNode(): BuilderNode<[Params]> | undefined | null{
return this.rootNode;
}
/**
* 更新BuilderNode節(jié)點
*/
updateNode(arg: Object): void {
this.rootNode?.update(arg);
}
/**
* 獲取EmbedId
*/
getEmbedId() : string {
return this.embedId;
}
/**
* 將觸摸事件派發(fā)到rootNode創(chuàng)建出的FrameNode上
*/
postEvent(event: TouchEvent | undefined) : boolean {
return this.rootNode?.postTouchEvent(event) as boolean;
}
}
- 添加同層渲染的組件。
@Component
struct SearchComponent {
@Prop params: Params;
controller: SearchController = new SearchController();
build() {
Column() {
Column({ space: MARGIN_VERTICAL }) {
Text($r('app.string.headline')).fontSize($r('app.string.ohos_id_text_size_body1'))
Text($r('app.string.illustrate')).fontSize($r('app.string.ohos_id_text_size_body1'))
}
// 原生Text組件
Text($r('app.string.mall')).fontSize($r('app.string.ohos_id_text_size_body1'))
// 原生Search組件
Search({ placeholder: 'Type to search...', controller: this.controller })
.searchButton(SEARCH_BUTTON)
// 原生Grid組件即寒,Grid中包含Image和Text
Grid() {
// 性能知識點:此處數(shù)據(jù)量確定且數(shù)量較少橡淆,使用了ForEach,在數(shù)據(jù)量多的情況下母赵,推薦使用LazyForeEach
ForEach(PRODUCT_DATA, (item: ProductDataModel, index: number) => {
GridItem() {
Column({ space: MARGIN_VERTICAL }) {
Image(item.uri).width($r('app.integer.image_size'))
Row({ space: MARGIN_VERTICAL }) {
Text(item.title).fontSize($r('app.string.ohos_id_text_size_body3'))
Text(item.price).fontSize($r('app.string.ohos_id_text_size_body3'))
}
}
}
})
}
.columnsTemplate('1fr 1fr') // 2列
.rowsTemplate('1fr 1fr ') // 2行
.rowsGap($r('app.string.ohos_id_elements_margin_vertical_m')) // 行間距
.columnsGap($r('app.string.ohos_id_elements_margin_vertical_m')) // 列間距
}
}
}
- embed標簽可以在H5頁面中嵌入任何類型的內(nèi)容逸爵,在H5界面上通過embed標簽標識同層元素,應用側(cè)會將原生組件渲染到H5頁面embed標簽所在位置凹嘲。
<div>
<div id="bodyId">
<!-- 在H5界面上通過embed標簽標識同層元素痊银,在應用側(cè)將原生組件渲染到H5頁面embed標簽所在位置-->
<embed id="nativeSearch" type = "native/component" width="100%" height="100%" src="view"/>
</div>
</div>
- 通過WebView的enableNativeEmbedMode()控制同層渲染開關,通過onNativeEmbedLifecycleChange獲取embed標簽的生命周期變化數(shù)據(jù)施绎。
build(){
Column() {
Stack() {
// 性能知識點:此處componentId項確定且數(shù)量較少溯革,使用了ForEach,在數(shù)據(jù)量多的情況下谷醉,推薦使用LazyForeEach
ForEach(this.componentIdArr, (componentId: string) => {
NodeContainer(this.nodeControllerMap.get(componentId));
}, (embedId: string) => embedId)
// web組件加載本地test.html頁面
Web({ src: $rawfile("view.html"), controller: this.browserTabController })
.backgroundColor($r('app.color.ohos_id_color_sub_background'))
// 不允許執(zhí)行縮放
.zoomAccess(false)
// Todo: 知識點:通過enableNativeEmbedMode()配置同層渲染開關
.enableNativeEmbedMode(true)
// Todo: 知識點:通過onNativeEmbedLifecycleChange獲取embed標簽的生命周期變化數(shù)據(jù)
.onNativeEmbedLifecycleChange((embed) => {
// 獲取web側(cè)embed元素的id
const componentId = embed.info?.id?.toString() as string
if (embed.status === NativeEmbedStatus.CREATE) {
// 創(chuàng)建節(jié)點控制器致稀,設置參數(shù)并rebuild
let nodeController = new SearchNodeController();
// 外接紋理與WebView同層渲染
nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string, renderType : NodeRenderType.RENDER_componentTypeTEXTURE, embedId : embed.embedId as string, width : px2vp(embed.info?.width), height : px2vp(embed.info?.height)});
nodeController.rebuild();
// 根據(jù)web傳入的embed的id屬性作為key,將nodeController存入map
this.nodeControllerMap.set(componentId, nodeController);
// 將web傳入的embed的id屬性存入@State狀態(tài)數(shù)組變量中俱尼,用于動態(tài)創(chuàng)建nodeContainer節(jié)點容器抖单,需要將push動作放在set之后
this.componentIdArr.push(componentId);
} else if (embed.status === NativeEmbedStatus.UPDATE) {
let nodeController = this.nodeControllerMap.get(componentId);
nodeController?.updateNode({text: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject);
nodeController?.rebuild();
} else {
let nodeController = this.nodeControllerMap.get(componentId);
nodeController?.setBuilderNode(null);
nodeController?.rebuild();
}
})// 獲取同層渲染組件觸摸事件信息
.onNativeEmbedGestureEvent((touch) => {
this.componentIdArr.forEach((componentId: string) => {
let nodeController = this.nodeControllerMap.get(componentId);
if (nodeController?.getEmbedId() === touch.embedId) {
nodeController?.postEvent(touch.touchEvent);
}
})
})
}
}
}
- h5側(cè)通過id名獲取embed標簽信息,并通過embed標簽添加同層渲染界面的touch監(jiān)聽事件;應用側(cè)添加onNativeEmbedGestureEvent回調(diào)使得手指觸摸到embed標簽時能獲取到觸摸事件信息遇八。
let nativeEmbed = {
// 通過id名獲取embed標簽
nativeSearch : document.getElementById('nativeSearch'),
// 事件
events:{},
// 初始化
init:function(){
let self = this;
// 添加touch的監(jiān)聽事件
self.nativeSearch.addEventListener('touchstart', self.events, false);
}
};
nativeEmbed.init();
Web({ src: $rawfile("view.html"), controller: this.browserTabController })
// 獲取同層渲染組件觸摸事件信息
.onNativeEmbedGestureEvent((touch) => {
this.componentIdArr.forEach((componentId: string) => {
let nodeController = this.nodeControllerMap.get(componentId);
if (nodeController?.getEmbedId() === touch.embedId) {
nodeController?.postEvent(touch.touchEvent);
}
})
})
高性能知識點
ArkWeb同層渲染原生組件矛绘,原生組件不僅可以提供H5組件無法實現(xiàn)的一些功能,還能提升用戶體驗的流暢度刃永;同層渲染節(jié)點上下樹货矮,實現(xiàn)節(jié)點復用,節(jié)省節(jié)點重復開銷斯够。
工程結構&模塊類型
nativeembed // har類型
|---mock
| |---GoodsMock.ets // 數(shù)據(jù)源
|---model
| |---GoodsModel.ets // 數(shù)據(jù)類
|---view
| |---NativeEmbedView.ets // 視圖層
模塊依賴
本實例依賴common模塊來實現(xiàn)資源的調(diào)用囚玫。 依賴動態(tài)路由模塊來實現(xiàn)頁面的動態(tài)加載。