命令式组件

ContentSlot接入

根据OpenHarmony关于XComponentType的介绍,先前在RNOH中使用的用于Native UI节点的占位容器XComponent的NODE类型不再演进,推荐使用ContentSlot占位组件管理Native API创建的组件,ContentSlot在内存和性能方面都优于NODE类型的XComponent。此外XComponent的libraryname参数不支持跨module使用,即无法找到其它模块中的同名so,RNOH的日志会提示找不到对应的so。因此,RNOH将使用ContentSlot作为占位组件。以下是使用ContentSlot接入的流程介绍:

请添加图片描述

CAPI版本使用 ContentSlot 总共分成了2个步骤:

  1. createSurface的时候创建ArkUISurface;
  2. startSurface 的时候将 CPP 的 ArkUISurface 连接到ArkUI的 NodeContent 上。

createSurface的时候主要做了以下的操作:

  1. 创建并将ArkUISurface记录到Map中:

    void RNInstanceCAPI::createSurface(
    facebook::react::Tag surfaceId,
    std::string const& moduleName) {
    m_surfaceById.emplace(
        surfaceId,
        ArkUISurface(
            ···
            surfaceId,
            moduleName));
    }
    
  2. ArkUISurface中创建nodeContentHandlerootView,通过attachToNodeContent来链接nodeContentHandlerootView,用于挂载 C-API组件,并在Surface上统一处理 Touch 事件:

    ArkUISurface::ArkUISurface(
        ...
        SurfaceId surfaceId,
        int rnInstanceId,
        std::string const& appKey)
        : m_surfaceId(surfaceId),
        m_scheduler(std::move(scheduler)),
        m_componentInstanceRegistry(std::move(componentInstanceRegistry)),
        m_surfaceHandler(SurfaceHandler(appKey, surfaceId)) {
    m_scheduler->registerSurface(m_surfaceHandler);
    m_taskExecutor = taskExecutor;
    m_rootView = componentInstanceFactory->create(
        surfaceId, facebook::react::RootShadowNode::Handle(), "RootView");
    if (m_rootView == nullptr) {
        LOG(ERROR) << "Couldn't create rootInstanceComponent for surface with id: "
                << surfaceId;
        return;
    }
    ...
    }
    
    
    void ArkUISurface::attachToNodeContent(NodeContentHandle nodeContentHandle) {
        m_threadGuard.assertThread();
        if (m_nodeContentHandle.has_value()) {
            throw RNOHError(
                "ArkUISurface: calling attachToNodeContent on an already attached Surface");
        }
        m_nodeContentHandle = nodeContentHandle;
        m_nodeContentHandle.value().addNode(m_rootView->getLocalRootArkUINode());
        HarmonyReactMarker::logMarker(
            HarmonyReactMarker::HarmonyReactMarkerId::CONTENT_APPEARED,
            m_rootView->getTag());
    }
    

startSurface 的时候主要做了以下的操作:

  1. 在 ArkTS 侧创建 ContentSlot,并在ContentSlot中传入rootViewNodeContentrootViewNodeContent为新建的NodeContent组件:

      // RNSurface.ets
      private rootViewNodeContent: NodeContent = new NodeContent();
      ...
      ...
      ...
      if (this.ctx.rnInstance.getArchitecture() === "C_API") {
        ContentSlot(this.rootViewNodeContent)
      }
    
    
  2. 调用RNOHAppNapiBridge.cpp中的attachRootView,将rootView添加到ArkTS侧的NodeContent上:

    static napi_value attachRootView(napi_env env, napi_callback_info info) {
        return invoke(env, [&] {
            ArkJS arkJS(env);
            auto args = arkJS.getCallbackArgs(info, 3);
            auto instanceId = arkJS.getInteger(args[0]);
            auto surfaceId = arkJS.getInteger(args[1]);
            auto nodeContentHandle = NodeContentHandle::fromNapiValue(env, args[2]);
            auto rnInstance = maybeGetInstanceById(instanceId);
            if (!rnInstance) {
            return arkJS.createFromRNOHError(
                RNOHError("Failed to get the RNInstance"));
            }
            auto rnInstanceCAPIRawPtr =
                std::dynamic_pointer_cast<RNInstanceCAPI>(rnInstance);
            if (rnInstanceCAPIRawPtr != nullptr) {
            rnInstanceCAPIRawPtr->attachRootView(
                std::move(nodeContentHandle), surfaceId);
            }
            return arkJS.getNull();
        });
    }
    
    
  3. 将 rootView 连接到 ContentSlot 后,rootView 就作为 CAPI 组件的根节点,后续的子孙节点通过 Mutation 指令逐个插入到组件树上。


在开发涉及原生模块(Native API)与前端框架(如React, Vue等)集成的Web应用时,经常需要使用到ContentSlot或Placeholder组件来管理原生组件的占位。这种需求尤其在微前端架构、渐进式Web应用(PWA)或跨平台应用中非常常见。下面将介绍如何在几种不同的前端框架中实现ContentSlot或Placeholder组件来管理原生组件的创建和展示。

在这里插入图片描述

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐