# ComponentContent ComponentContent表示组件内容的实体封装,其对象支持在非UI组件中创建与传递,便于开发者对弹窗类组件进行解耦封装。其底层使用了BuilderNode,具体使用规格参考[BuilderNode](js-apis-arkui-builderNode.md)。 > **说明:** > > 本模块从API version 12开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 > > 当前不支持在预览器中使用ComponentContent。 ## 导入模块 ```ts import { ComponentContent } from '@kit.ArkUI'; ``` ## ComponentContent 继承自[Content](js-apis-arkui-Content.md#content-1)。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full ### constructor constructor(uiContext: UIContext, builder: WrappedBuilder\<[]>) ComponentContent的构造函数。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | --------- | ----------------------------------------- | ---- | ---------------------------------- | | uiContext | [UIContext](./arkts-apis-uicontext-uicontext.md) | 是 | 创建对应节点时所需要的UI上下文。 | | builder | [WrappedBuilder\<[]>](../../ui/state-management/arkts-wrapBuilder.md) | 是 | 封装不带参builder函数的WrappedBuilder对象。 | ### constructor constructor(uiContext: UIContext, builder: WrappedBuilder\<[T]>, args: T) ComponentContent的构造函数。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | --------- | ----------------------------------------- | ---- | ---------------------------------- | | uiContext | [UIContext](./arkts-apis-uicontext-uicontext.md) | 是 | 创建对应节点时候所需要的UI上下文。 | | builder | [WrappedBuilder\<[T]>](../../ui/state-management/arkts-wrapBuilder.md) | 是 | 封装带参builder函数的WrappedBuilder对象。 | | args | T | 是 | WrappedBuilder对象封装的builder函数的参数。 | ### constructor constructor(uiContext: UIContext, builder: WrappedBuilder\<[T]>, args: T, options: BuildOptions) ComponentContent的构造函数。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | --------- | ----------------------------------------- | ---- | ---------------------------------- | | uiContext | [UIContext](./arkts-apis-uicontext-uicontext.md) | 是 | 创建对应节点时候所需要的UI上下文。 | | builder | [WrappedBuilder\<[T]>](../../ui/state-management/arkts-wrapBuilder.md) | 是 | 封装带参builder函数的WrappedBuilder对象。 | | args | T | 是 | WrappedBuilder对象封装的builder函数的参数。 | | options | [BuildOptions](./js-apis-arkui-builderNode.md#buildoptions12) | 是 | build的配置参数,判断是否支持@Builder中嵌套@Builder的行为。 | **示例:** ``` ts import { ComponentContent, NodeContent, typeNode } from "@kit.ArkUI"; interface ParamsInterface { text: string; func: Function; } @Builder function buildTextWithFunc(fun: Function) { Text(fun()) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) } @Builder function buildText(params: ParamsInterface) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) buildTextWithFunc(params.func) } } @Entry @Component struct Index { @State message: string = "HELLO"; private content: NodeContent = new NodeContent(); build() { Row() { Column() { Button('addComponentContent') .onClick(() => { let column = typeNode.createNode(this.getUIContext(), "Column"); column.initialize(); column.addComponentContent(new ComponentContent(this.getUIContext(), wrapBuilder<[ParamsInterface]>(buildText), { text: this.message, func: () => { return "FUNCTION" } }, { nestingBuilderSupported: true })); this.content.addFrameNode(column); }) ContentSlot(this.content) } .id("column") .width('100%') .height('100%') } .height('100%') } } ``` ### update update(args: T): void 用于更新[WrappedBuilder](../../ui/state-management/arkts-wrapBuilder.md)对象封装的builder函数参数,与constructor传入的参数类型保持一致。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | ------ | ---- | ---- | ------------------------------------------------------------ | | args | T | 是 | 用于更新[WrappedBuilder](../../ui/state-management/arkts-wrapBuilder.md)对象封装的builder函数参数,与constructor传入的参数类型保持一致。 | **示例:** ```ts import { ComponentContent } from "@kit.ArkUI"; class Params { text: string = ""; constructor(text: string) { this.text = text; } } @Builder function buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({bottom: 36}) }.backgroundColor('#FFF0F0F0') } @Entry @Component struct Index { @State message: string = "hello"; build() { Row() { Column() { Button("click me") .onClick(() => { let uiContext = this.getUIContext(); let promptAction = uiContext.getPromptAction(); let contentNode = new ComponentContent(uiContext, wrapBuilder(buildText), new Params(this.message)); promptAction.openCustomDialog(contentNode); setTimeout(() => { contentNode.update(new Params("new message")); }, 2000); //2秒后自动更新弹窗内容文本 }) } .width('100%') .height('100%') } .height('100%') } } ``` ### reuse reuse(param?: Object): void 触发ComponentContent中的自定义组件的复用。组件复用请参见[@Reusable装饰器:组件复用](../../ui/state-management/arkts-reusable.md)。关于ComponentContent的解绑场景请参见[解除实体节点引用关系](../../ui/arkts-user-defined-arktsNode-builderNode.md#解除实体节点引用关系)。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | ------ | ------ | ---- | ------------------------------------------------------------------------ | | param | Object | 否 | 用于复用WrappedBuilder对象封装的builder函数参数,与constructor传入的参数类型保持一致。 | ### recycle recycle(): void - 触发ComponentContent中自定义组件的回收。自定义组件的回收是组件复用机制中的环节,具体信息请参见[@Reusable装饰器:组件复用](../../ui/state-management/arkts-reusable.md)。 - ComponentContent通过reuse和recycle完成其内外自定义组件之间的复用事件传递,具体使用场景请参见[BuilderNode调用reuse和recycle接口实现节点复用能力](../../ui/arkts-user-defined-arktsNode-builderNode.md#buildernode调用reuse和recycle接口实现节点复用能力)。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full ```ts import { NodeContent, typeNode, ComponentContent } from "@kit.ArkUI"; const TEST_TAG: string = "Reuse+Recycle"; class MyDataSource { private dataArray: string[] = []; private listener: DataChangeListener | null = null; public totalCount(): number { return this.dataArray.length; } public getData(index: number) { return this.dataArray[index]; } public pushData(data: string) { this.dataArray.push(data); } public reloadListener(): void { this.listener?.onDataReloaded(); } public registerDataChangeListener(listener: DataChangeListener): void { this.listener = listener; } public unregisterDataChangeListener(): void { this.listener = null; } } class Params { item: string = ''; constructor(item: string) { this.item = item; } } @Builder function buildNode(param: Params = new Params("hello")) { Row() { Text(`C${param.item} -- `) ReusableChildComponent2({ item: param.item }) //该自定义组件在ComponentContent中无法被正确复用 } } // 被回收复用的自定义组件,其状态变量会更新,而子自定义组件ReusableChildComponent3中的状态变量也会更新,但ComponentContent会阻断这一传递过程 @Reusable @Component struct ReusableChildComponent { @Prop item: string = ''; @Prop switch: string = ''; private content: NodeContent = new NodeContent(); private componentContent: ComponentContent = new ComponentContent( this.getUIContext(), wrapBuilder<[Params]>(buildNode), new Params(this.item), { nestingBuilderSupported: true }); aboutToAppear() { let column = typeNode.createNode(this.getUIContext(), "Column"); column.initialize(); column.addComponentContent(this.componentContent); this.content.addFrameNode(column); } aboutToRecycle(): void { console.info(`${TEST_TAG} ReusableChildComponent aboutToRecycle ${this.item}`); // 当开关为open,通过ComponentContent的reuse接口和recycle接口传递给其下的自定义组件,例如ReusableChildComponent2,完成复用 if (this.switch === 'open') { this.componentContent.recycle(); } } aboutToReuse(params: object): void { console.info(`${TEST_TAG} ReusableChildComponent aboutToReuse ${JSON.stringify(params)}`); // 当开关为open,通过ComponentContent的reuse接口和recycle接口传递给其下的自定义组件,例如ReusableChildComponent2,完成复用 if (this.switch === 'open') { this.componentContent.reuse(params); } } build() { Row() { Text(`A${this.item}--`) ReusableChildComponent3({ item: this.item }) ContentSlot(this.content) } } } @Component struct ReusableChildComponent2 { @Prop item: string = "false"; aboutToReuse(params: Record) { console.info(`${TEST_TAG} ReusableChildComponent2 aboutToReuse ${JSON.stringify(params)}`); } aboutToRecycle(): void { console.info(`${TEST_TAG} ReusableChildComponent2 aboutToRecycle ${this.item}`); } build() { Row() { Text(`D${this.item}`) .fontSize(20) .backgroundColor(Color.Yellow) .margin({ left: 10 }) }.margin({ left: 10, right: 10 }) } } @Component struct ReusableChildComponent3 { @Prop item: string = "false"; aboutToReuse(params: Record) { console.info(`${TEST_TAG} ReusableChildComponent3 aboutToReuse ${JSON.stringify(params)}`); } aboutToRecycle(): void { console.info(`${TEST_TAG} ReusableChildComponent3 aboutToRecycle ${this.item}`); } build() { Row() { Text(`B${this.item}`) .fontSize(20) .backgroundColor(Color.Yellow) .margin({ left: 10 }) }.margin({ left: 10, right: 10 }) } } @Entry @Component struct Index { @State data: MyDataSource = new MyDataSource(); aboutToAppear() { for (let i = 0; i < 100; i++) { this.data.pushData(i.toString()); } } build() { Column() { List({ space: 3 }) { LazyForEach(this.data, (item: string) => { ListItem() { ReusableChildComponent({ item: item, switch: 'open' // 将open改为close可观察到,ComponentContent不通过reuse和recycle接口传递复用时,ComponentContent内部的自定义组件的行为表现 }) } }, (item: string) => item) } .width('100%') .height('100%') } } } ``` ### dispose dispose(): void 立即释放当前ComponentContent对象对[基本概念:实体节点](../../ui/arkts-user-defined-node.md#基本概念)的引用关系。关于ComponentContent的解绑场景请参见[解除实体节点引用关系](../../ui/arkts-user-defined-arktsNode-builderNode.md#解除实体节点引用关系)。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full > **说明:** > > 当ComponentContent对象调用dispose之后,会与后端实体节点解除引用关系。若前端对象ComponentContent无法释放,容易导致内存泄漏。建议在不再需要操作该ComponentContent对象时,开发者主动调用dispose释放后端节点,以减少引用关系的复杂性,降低内存泄漏的风险。 **示例:** ```ts import { BusinessError } from '@kit.BasicServicesKit'; import { ComponentContent } from '@kit.ArkUI'; class Params { text: string = ""; constructor(text: string) { this.text = text; } } @Builder function buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) }.backgroundColor('#FFF0F0F0') } @Entry @Component struct Index { @State message: string = "hello"; build() { Row() { Column() { Button("click me") .onClick(() => { let uiContext = this.getUIContext(); let promptAction = uiContext.getPromptAction(); let contentNode = new ComponentContent(uiContext, wrapBuilder(buildText), new Params(this.message)); promptAction.openCustomDialog(contentNode); setTimeout(() => { promptAction.closeCustomDialog(contentNode) .then(() => { console.info('customDialog closed.'); if (contentNode !== null) { contentNode.dispose(); //释放contentNode } }).catch((error: BusinessError) => { let message = (error as BusinessError).message; let code = (error as BusinessError).code; console.error(`closeCustomDialog args error code is ${code}, message is ${message}`); }) }, 2000); //2秒后自动关闭 }) } .width('100%') .height('100%') } .height('100%') } } ``` ### updateConfiguration updateConfiguration(): void 传递[系统环境变化](../apis-ability-kit/js-apis-app-ability-configuration.md)事件,触发节点的全量更新。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full > **说明:** > > updateConfiguration接口用于通知对象更新当前的系统环境变化。 **示例:** ```ts import { NodeController, FrameNode, ComponentContent, UIContext, FrameCallback } from '@kit.ArkUI'; import { AbilityConstant, Configuration, EnvironmentCallback, ConfigurationConstant } from '@kit.AbilityKit'; @Builder function buildText() { Column() { Text('Hello') .fontSize(36) .fontWeight(FontWeight.Bold) } .backgroundColor($r('sys.color.ohos_id_color_background')) .width('100%') .alignItems(HorizontalAlign.Center) .padding(16) } const componentContentMap: Array> = new Array(); class MyNodeController extends NodeController { private rootNode: FrameNode | null = null; makeNode(uiContext: UIContext): FrameNode | null { return this.rootNode; } createNode(context: UIContext) { this.rootNode = new FrameNode(context); let component = new ComponentContent(context, wrapBuilder(buildText)); componentContentMap.push(component); this.rootNode.addComponentContent(component); } deleteNode() { let node = componentContentMap.pop(); this.rootNode?.dispose(); node?.dispose(); } } class MyFrameCallback extends FrameCallback { onFrame() { updateColorMode(); } } function updateColorMode() { componentContentMap.forEach((value, index) => { value.updateConfiguration(); }) } @Entry @Component struct FrameNodeTypeTest { private myNodeController: MyNodeController = new MyNodeController(); aboutToAppear(): void { let environmentCallback: EnvironmentCallback = { onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => { console.info('onMemoryLevel'); }, onConfigurationUpdated: (config: Configuration): void => { console.info(`onConfigurationUpdated ${config}`); this.getUIContext()?.postFrameCallback(new MyFrameCallback()); } } // 注册监听回调 this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback); // 设置应用深浅色跟随系统 this.getUIContext() .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); this.myNodeController.createNode(this.getUIContext()); } aboutToDisappear(): void { //移除map中的引用,并将自定义节点释放 this.myNodeController.deleteNode(); } build() { Column({ space: 16 }) { NodeContainer(this.myNodeController); Button('切换深色') .onClick(() => { this.getUIContext() .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK); }) Button('设置浅色') .onClick(() => { this.getUIContext() .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT); }) } } } ``` ### isDisposed20+ isDisposed(): boolean 查询当前ComponentContent对象是否已解除与后端实体节点的引用关系。前端节点均绑定有相应的后端实体节点,当节点调用dispose接口解除绑定后,再次调用接口可能会出现crash、返回默认值的情况。由于业务需求,可能存在节点在dispose后仍被调用接口的情况。为此,提供此接口以供开发者在操作节点前检查其有效性,避免潜在风险。 **原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **返回值:** | 类型 | 说明 | | ------- | ------------------ | | boolean | 后端实体节点是否解除引用。true为节点已与后端实体节点解除引用,false为节点未与后端实体节点解除引用。 **示例:** ```ts import { BusinessError } from '@kit.BasicServicesKit'; import { ComponentContent } from '@kit.ArkUI'; class Params { text: string = ""; constructor(text: string) { this.text = text; } } @Builder function buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) }.backgroundColor('#FFF0F0F0') } @Entry @Component struct Index { @State message: string = "hello"; @State beforeDispose: string = '' @State afterDispose: string = '' build() { Row() { Column() { Button("click me") .onClick(() => { let uiContext = this.getUIContext(); let promptAction = uiContext.getPromptAction(); let contentNode = new ComponentContent(uiContext, wrapBuilder(buildText), new Params(this.message)); promptAction.openCustomDialog(contentNode); setTimeout(() => { promptAction.closeCustomDialog(contentNode) .then(() => { console.info('customDialog closed.'); if (contentNode !== null) { this.beforeDispose = contentNode.isDisposed() ? 'before dispose componentContent isDisposed is true' : 'before dispose componentContent isDisposed is false'; contentNode.dispose(); //释放contentNode this.afterDispose = contentNode.isDisposed() ? 'after dispose componentContent isDisposed is true' : 'after dispose componentContent isDisposed is false'; } }).catch((error: BusinessError) => { let message = (error as BusinessError).message; let code = (error as BusinessError).code; console.error(`closeCustomDialog args error code is ${code}, message is ${message}`); }) }, 1000); //1秒后自动关闭 }) Text(this.beforeDispose) .fontSize(25) .margin({ top: 10, bottom: 10 }) Text(this.afterDispose) .fontSize(25) } .width('100%') .height('100%') } .height('100%') } } ``` ![](figures/component_content_isDisposed.gif) ### inheritFreezeOptions20+ inheritFreezeOptions(enabled: boolean): void 查询当前ComponentContent对象是否设置为继承父组件中自定义组件的冻结策略。如果设置继承状态为false,则ComponentContent对象的冻结策略为false。在这种情况下,节点在不活跃状态下不会被冻结。 **原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | ------ | ------ | ---- | ------------------------------------------------------------------------ | | enabled | boolean | 是 | ComponentContent对象是否设置为继承父组件中自定义组件的冻结策略。true为继承父组件中自定义组件的冻结策略,false为不继承父组件中自定义组件的冻结策略。 | **示例:** ```ts import { ComponentContent, FrameNode, NodeController } from '@kit.ArkUI'; class Params { count: number = 0; constructor(count: number) { this.count = count; } } @Builder // builder组件 function buildText(params: Params) { Column() { TextBuilder({ message: params.count }) } } class TextNodeController extends NodeController { private rootNode: FrameNode | null = null; private contentNode: ComponentContent | null = null; private count: number = 0; makeNode(context: UIContext): FrameNode | null { this.rootNode = new FrameNode(context); this.contentNode = new ComponentContent(context, wrapBuilder(buildText), new Params(this.count)); // 通过buildText创建ComponentContent this.contentNode.inheritFreezeOptions(true); // 设置ComponentContent的冻结继承状态为True if (this.rootNode !== null) { this.rootNode.addComponentContent(this.contentNode); // 将ComponentContent上树 } return this.rootNode; } update(): void { if (this.contentNode !== null) { this.count += 1; this.contentNode.update(new Params(this.count)); // 更新ComponentContent中的数据,可以触发Log } } } const textNodeController: TextNodeController = new TextNodeController(); @Entry @Component struct MyNavigationTestStack { @Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack(); @State message: number = 0; @State logNumber: number = 0; @Builder PageMap(name: string) { if (name === 'pageOne') { pageOneStack({ message: this.message, logNumber: this.logNumber }) } else if (name === 'pageTwo') { pageTwoStack({ message: this.message, logNumber: this.logNumber }) } } build() { Column() { Button('update ComponentContent') // 点击更新ComponentContent .onClick(() => { textNodeController.update(); }) Navigation(this.pageInfo) { Column() { Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) .width('80%') .height(40) .margin(20) .onClick(() => { this.pageInfo.pushPath({ name: 'pageOne' }); // 将name指定的NavDestination页面信息入栈 }) } }.title('NavIndex') .navDestination(this.PageMap) .mode(NavigationMode.Stack) } } } @Component struct pageOneStack { // 页面一 @Consume('pageInfo') pageInfo: NavPathStack; @State index: number = 1; @Link message: number; @Link logNumber: number; build() { NavDestination() { Column() { NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber }) Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) // 切换至页面二 .width('80%') .height(40) .margin(20) .onClick(() => { this.pageInfo.pushPathByName('pageTwo', null); }) Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) // 返回主页面 .width('80%') .height(40) .margin(20) .onClick(() => { this.pageInfo.pop(); }) }.width('100%').height('100%') }.title('pageOne') .onBackPressed(() => { this.pageInfo.pop(); return true; }) } } @Component struct pageTwoStack { // 页面二 @Consume('pageInfo') pageInfo: NavPathStack; @State index: number = 2; @Link message: number; @Link logNumber: number; build() { NavDestination() { Column() { NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber }) Text('BuilderNode处于冻结') .fontWeight(FontWeight.Bold) .margin({ top: 48, bottom: 48 }) Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) // 返回至页面一 .width('80%') .height(40) .margin(20) .onClick(() => { this.pageInfo.pop(); }) }.width('100%').height('100%') }.title('pageTwo') .onBackPressed(() => { this.pageInfo.pop(); return true; }) } } @Component({ freezeWhenInactive: true }) // 设置冻结策略为不活跃冻结 struct NavigationContentMsgStack { @Link message: number; @Link index: number; @Link logNumber: number; build() { Column() { if (this.index === 1) { NodeContainer(textNodeController) } } } } @Component({ freezeWhenInactive: true }) // 设置冻结策略为不活跃冻结 struct TextBuilder { @Prop @Watch("info") message: number = 0; info() { console.info(`freeze-test TextBuilder message callback ${this.message}`); // 根据message内容变化来打印日志来判断是否冻结 } build() { Row() { Column() { Text(`文本更新次数: ${this.message}`) .fontWeight(FontWeight.Bold) .margin({ top: 48, bottom: 48 }) } } } } ``` ![](figures/component_content_inheritFreezeOptions.gif)