# node_pool **Repository Path**: openharmony-sig/node_pool ## Basic Information - **Project Name**: node_pool - **Description**: 创建全局的自定义组件复用池,实现跨页面的组件复用 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://gitee.com/openharmony-sig/node_pool - **GVP Project**: No ## Statistics - **Stars**: 15 - **Forks**: 11 - **Created**: 2024-09-05 - **Last Updated**: 2025-05-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # node_pool **专门为OpenHarmony打造的一款全局自定义组件复用的三方库,致力于更高效、更轻便、更简单。** ## 简介 通过BuilderNode创建全局的自定义组件复用池,实现跨页面的组件复用。 ## 效果展示 ![预览图](screenshots/sample.gif) ## 安装教程 ``` ohpm install @hadss/nodepool ``` ## 使用说明 ### 创建可复用的自定义组件 ```typescript import { NodeItem, CustomNodePoolFactory } from '@hadss/nodepool'; class Params { text: string = "this is a text"; constructor(text: string) { this.text = text; } } @Builder function ButtonBuilder(params: Params) { Column() { Button(`button ` + params.text) .borderWidth(2) .backgroundColor(Color.Orange) .width("100%") .height("100%") .gesture( TapGesture() .onAction((event: GestureEvent) => { console.log("TapGesture"); }) ) } .width('100%') .height(300) .backgroundColor(Color.Gray) } ``` ### 创建Builder及复用池类型 ```typescript let btnBuilder: WrappedBuilder = wrapBuilder(ButtonBuilder); const REUSE_VIEW_TYPE_SWIPER: string = 'reuse_type_swiper_'; ``` ### 创建一个CustomNodePoolFactory类型的对象,通过多例模式预创建NodePool复用池,同时通过单例模式获取创建NodePool组件复用池,根据传入的type类型查找复用池中是否存在可复用的组件,如果有则直接使用,如果没有则重新创建。使用NodeContainerProxy组件占位,从复用池NodePool中获取组件加载到页面中 ```typescript @Entry @Component struct Index { private nodePoolFactory: CustomNodePoolFactory = new CustomNodePoolFactory(); private controller: NodeItem | undefined; private typeCfg: TypeReuseConfig = { type: REUSE_VIEW_TYPE_SWIPER, expirationTime: 30 * 60 * 1000, // 老化时间 reuseCallback: this.reuseCallback, recycleCallback: this.recycleCallback } // 组件复用生命周期回调 private reuseCallback(item: NodeItem): void { console.log('tag1', `reuseCallback, id:${item.id}`); } // 组件回收生命周期回调 private recycleCallback(item: NodeItem): void { console.log('tag1', `recycleCallback, id:${item.id}`); } aboutToAppear(): void { this.nodePoolFactory.getCommonNodePool().setTypeReuseConfig(this.typeCfg); // 组件复用 this.controller = this.nodePoolFactory.getCommonNodePool().getNode(REUSE_VIEW_TYPE_SWIPER, { text: 'hello' }, btnBuilder); } build() { Column() { NodeContainerProxy({ nodeItem: this.controller }) Text("点击进行参数传递和触摸事件传递") .width('100%') .height(300) .backgroundColor(Color.Pink) .onTouch((event) => { if (event != undefined) { this.controller?.postTouchEvent(event); // 触摸事件传递 this.controller?.node?.update(new Params("on update data")); // 参数传递 } }) } } } ``` ## 接口说明 ### NodeContainerProxy组件 | 参数名称 | 入参内容 | 功能简介 | |----------|---------------------|--------------| | nodeItem | NodeItem, undefined | 节点Controller | ### CustomNodePoolFactory接口 | 接口名称 | 入参内容 | 功能简介 | |-------------------|----------------|---------| | getCommonNodePool | NA | 获取单例节点池 | | getNodePool | NodePoolConfig | 获取多例节点池 | ### NodePoolConfig参数列表 | 参数名称 | 入参内容 | 功能简介 | |-----------------|-----------------|--------------------| | typeCount | number | 节点种类数量,默认值:50 | | nodeSize | number | 某种节点数量,默认值:100 | | expirationTime | number | 老化时间,默认值:24小时。单位ms | | reuseCallback | ReuseCallback | 复用时回调函数 | | recycleCallback | RecycleCallback | 回收时回调函数 | ### NodePool接口 | 接口名称 | 入参内容 | 功能简介 | |--------------------|---------------------------------------------------------------------------------------|-------------------| | getNode | type: string, data: ESObject, builder: WrappedBuilder | 获取节点Controller | | getWebNode | data: WebData, builder?: WrappedBuilder | 获取web节点Controller | | preCreateNode | type: string, data: ESObject, builder: WrappedBuilder, uiContext: UIContext | 预创建节点进池 | | preCreateWebNode | data: WebData, builder?: WrappedBuilder | 预创建web节点进池 | | setTypeReuseConfig | typeCfg: TypeReuseConfig | 设置不同类型节点的老化时间 | | setRecycleCallback | recycleCallback: RecycleCallback | 设置回收回调 | | setReuseCallback | reuseCallback: ReuseCallback | 设置复用回调 |
注意事项:preCreateNode预创建节点进池需要注意数量限制,如果无限创建会导致节点池爆满 ### NodePool参数列表 | 参数名称 | 入参内容 | 功能简介 | |---------|--------------------------|-------------------------| | type | string | 节点种类,可自定义 | | data | ESObject | 节点数据 | | builder | WrappedBuilder | 创建节点树所需的无状态UI方法@Builder | ### TypeReuseConfig参数列表 | 参数名称 | 入参内容 | 功能简介 | |-----------------|---------------|--------------| | type | string | 节点种类,可自定义 | | expirationTime | number | 老化时间,可自定义 | | reuseCallback | ReuseCallback | 组件复用回调函数,可为空 | | recycleCallback | ReuseCallback | 组件回收回调函数,可为空 | ### WebData参数列表 | 参数名称 | 入参内容 | 功能简介 | |------------|-------------------|---------------------| | url | string | url地址 | | controller | WebviewController | webview控制器 | | onActive | boolean | 是否进入前台激活状态,默认值:true | | uiContext | UIContext | UI上下文实例 | | data | ESObject | 业务参数,如:组件属性值 |
注意事项:针对组件复用过程中出现的图片闪动(老图片变为新图片的闪动),通过在原始数据中额外增加一个状态变量控制Image组件的渲染, 在三方库预制的recycleCallback和reuseCallback回调中更改这个状态,通知Image组件清空内部缓存数据来解决该问题。 ### 设置节点复用时回调 ```typescript let reuseCallback = (item: NodeItem) => { hilog.info('testTag', 'setReuseCallback'); } let nodePool = nodePoolFactory.getCommonNodePool(); nodePool.setReuseCallback(reuseCallback); ``` ### 设置节点回收时回调 ```typescript let recycleCallback = (item: NodeItem) => { hilog.info('testTag', 'setRecycleCallback'); } let nodePool = nodePoolFactory.getCommonNodePool(); nodePool.setRecycleCallback(recycleCallback); ``` ## web组件预渲染使用方法
  1. 构建wrapBuilder和获取nodepool与上述流程一致
  2. 在web的上级页面进行web页面的预创建(具体页面根据业务场景设定) ```typescript aboutToAppear(): void { webData.uiContext = this.getUIContext(); nodepool.preCreateWebNode(webData, warp); } ```
  3. 在web组件页面调用getWebNode获取创建的节点 ```typescript item: NodeItem | undefined = nodepool.getWebNode(webData, warp); Column() { NodeContainerProxy({ nodeItem: this.item }) } ```
  4. 在web组件上开启onActive实现预渲染(只需要预启动效果可不做) ```typescript Web({ src: data.url, controller: data.controller }) .width('100%') .height('100%') .geolocationAccess(false) .onPageBegin(() => { if (data.onActive) { data.controller.onActive() } }) ```
## XComponentWithImage用于解决图片闪动的使用说明
  1. 数据类继承XComponentWithImage
    ```typescript @Observed export class ViewItem extends XComponentWithImage { index: number = 0; // 索引 type: string = ''; // 卡片类型 title: string = ''; // 标题 coverImage: Resource | string = ''; // 封面图片 ... } ```
  2. 在提供的两个回调函数中新增是否清除图片的状态
    ```typescript private reuseCallback(item: NodeItem): void { if (item?.data?.item instanceof ViewItem) { let newItem: ViewItem = item.data.item as ViewItem; newItem.shouldClearImageCache = false; } } private recycleCallback(item: NodeItem): void { if (item?.data?.item instanceof ViewItem) { let newItem: ViewItem = item.data.item as ViewItem; newItem.shouldClearImageCache = true; } } ```
  3. 在图片组件中使用getImageSource方法
    ```typescript Image(this.item.getImageSource(this.item.coverImage)) ```
## 新增特性 1. 新增web节点适配,支持web预渲染能力 ## 约束与限制 在下述版本验证通过:
  • 本示例仅支持标准系统上运行,支持设备:华为手机。
  • DevEco Studio:NEXT 5.0.0 Release(5.0.3.910), SDK: API 12(5.0.0.71)。
  • DevEco Studio:NEXT Beta1(5.0.3.906), SDK: API12 (5.0.0.71)。 ## 目录结构 ``` |---- node_pool | |---- entry # 示例代码文件夹 | |---- nodepool # nodepool库文件夹 | |---- index.ets # 对外接口 │ └--src/main/ets # 框架代码目录 │ ├----constants # 常量目录 │ │ │ ├----lib # 组件复用三方库核心代码目录 │ │ │ ├----model # 各类型图表目录 │ │ │ └----utils # 工具类目录 │ └─---- README.md # 安装使用方法 ``` ## 参与贡献 使用过程中发现任何问题都可以提 [Issue](https://gitee.com/openharmony-sig/node_pool/issues) 给我们,当然,我们也非常欢迎你给我们发 [PR](https://gitee.com/openharmony-sig/node_pool/pulls) 。 ## 开源协议 本项目基于 [Apache License 2.0](https://gitee.com/openharmony-sig/node_pool/blob/master/nodepool/LICENSE) ,请自由地享受和参与开源。