哪位大佬可以提供HarmonyOS Next组件预创建示例代码?

阅读 599
avatarAI BotBETA

以下是一个HarmonyOS Next组件预创建的示例代码,该示例展示了如何通过BuilderNode实现全局自定义组件复用池,从而实现组件的预创建和复用:

// BuilderNodePool.ets
import { NodeController, FrameNode, UIContext, WrappedBuilder, HashMap, HashSet, UpdaterCallback, NodeItem as BaseNodeItem } from '@ohos.arkui';

export class NodePool {
    private static instance: NodePool;
    private nodePool: HashMap<string, Array<BaseNodeItem>>;
    private nodeHook: HashSet<BaseNodeItem>;
    private idGen: number;

    private constructor() {
        this.nodePool = new HashMap();
        this.nodeHook = new HashSet();
        this.idGen = 0;
    }

    // 单例模式,可以全局统一管理
    public static getInstance(): NodePool {
        if (!NodePool.instance) {
            NodePool.instance = new NodePool();
        }
        return NodePool.instance;
    }

    // 获取节点,如果不存在则创建新的节点
    public getNode(type: string, data: ESObject, builder: WrappedBuilder<ESObject>): BaseNodeItem | undefined {
        let node: BaseNodeItem | undefined = this.nodePool.get(type)?.pop();
        if (!node) {
            node = new NodeItem(builder, data, type);
            this.nodeHook.add(node);
        } else {
            node.update(data);
        }
        node.data.callback = (callback: UpdaterCallback) => {
            if (node) {
                node.registerUpdater(callback);
            }
        };
        return node;
    }

    // 回收节点,提供给下次复用
    public recycleNode(type: string, node: BaseNodeItem) {
        let nodeArray: Array<BaseNodeItem> = this.nodePool.get(type);
        if (!nodeArray) {
            nodeArray = new Array();
            this.nodePool.set(type, nodeArray);
        }
        nodeArray.push(node);
    }
}

export class NodeItem extends NodeController {
    private callback: UpdaterCallback | null = null;
    private data: ESObject;
    private builder: WrappedBuilder<ESObject>;
    private type: string;

    constructor(builder: WrappedBuilder<ESObject>, data: ESObject, type: string) {
        super();
        this.builder = builder;
        this.data = data;
        this.type = type;
    }

    // 父类方法,用于创建子组件
    makeNode(uiContext: UIContext): FrameNode | null {
        if (!this.node) {
            this.node = new FrameNode(uiContext);
            this.node.build(this.builder, this.data);
        } else {
            this.node.update(this.data);
        }
        return this.node;
    }

    // 更新数据
    update(data: ESObject): void {
        this.data = data;
        if (this.node) {
            this.node.update(data);
        }
    }

    // 组件隐藏时回收组件
    aboutToDisappear(): void {
        NodePool.getInstance().recycleNode(this.type, this);
    }

    // 注册更新回调
    registerUpdater(callback: UpdaterCallback): void {
        this.callback = callback;
    }
}

// 使用示例
@Entry
@Component
struct BuilderNodePoolDemo {
    @State array: Array<string> = ['Tab1', 'Tab2', 'Tab3'];
    private swiperController: any;

    build() {
        Column() {
            Swiper(this.swiperController) {
                LazyForEach(this.array, () => {
                    TabNode()
                }, (title: string) => title)
            }
            .width('100%')
            .height('100%')
        }
    }
}

@Component
struct TabNode {
    build() {
        NodeContainer(NodePool.getInstance().getNode('reuse_type_', { /* 数据 */ }, this.builder))
    }
}

解释

  1. NodePool类:这是一个单例模式的组件复用池,用于管理预创建的组件节点。它提供了getNoderecycleNode两个方法,分别用于获取和回收组件节点。
  2. NodeItem类:这是一个继承自NodeController的自定义组件类,它实现了makeNodeupdateaboutToDisappear等方法,用于创建、更新和回收组件节点。
  3. BuilderNodePoolDemo结构:这是一个示例页面,它使用SwiperLazyForEach来创建多个可切换的页面(或称为Tab)。每个Tab都使用NodeContainer来加载从复用池中获取的组件。
  4. TabNode结构:这是一个简单的结构,它使用NodeContainer来占位,并从复用池中获取组件加载到页面中。

通过上述代码,你可以实现HarmonyOS Next中的组件预创建和复用,从而提高应用的性能和响应速度。

1 个回答

下面的代码,模拟了应用冷启动的流程,在应用启动后先进入广告页(Index页面),并在广告页进行组件预创建。

//1. 在自定义组件复用池中实现预创建。
// 继承NodeController,创建可以复用的子组件
export class NodeItem extends NodeController {
  // ...

  // 预创建BuildNode
  prebuild(uiContext: UIContext) {
    this.node = new BuilderNode(uiContext);
    this.node.build(this.builder, this.data);
  }
}

// 全局组件复用池
export class NodePool {
  // ...

  public preBuild(type: string, item: ESObject, builder: WrappedBuilder<ESObject>, uiContext: UIContext) {
    if (type) {
      let nodeItem: NodeItem | undefined = new NodeItem();
      nodeItem.builder = builder;
      nodeItem.data.data = item;
      nodeItem.type = type;
      // 预创建组件
      nodeItem.prebuild(uiContext);
      // 将预创建的组件回收到复用池中,便于后续复用
      this.recycleNode(type, nodeItem);
    }
  }

  // ...
}

//2. 在广告页中预创建组件。
@Entry
@Component
struct Index {
  // ...
  aboutToAppear(): void {
    // ...
    // 获取模拟数据
    let viewItems: ViewItem[] = [];
    viewItems.push(...furnitureData());
    viewItems.push(...natureData());
    // 遍历模拟数据,预创建对应数量的组件
    viewItems.forEach((item) => {
      NodePool.getInstance()
        .preBuild('reuse_type_', item, flowItemWrapper, this.getUIContext());
    })
  }

  // ...
}

不过这种方式在冷启动时进行预创建,当组件数量较多时,会引起主线程的阻塞,增加冷启动耗时
本文参与了 【 HarmonyOS NEXT 技术问答冲榜,等你来战!】欢迎正在阅读的你也加入。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题