ReactNative Fabric渲染器和组件(5)

ReactNative Fabric渲染器和组件

简述

Fabric是ReactNative中新架构最核心的模块,本章我们会来了解一下自定义一个Fabric组件,然后在JS文件中声明之后如何,是怎么映射到原生构建一个View的。
关于Fabric架构理念官网已经有说明了,我们就不细说了,从实现来说,就是在JS层会有一套DOM树,这个是基于React实现的,然后在C++层会构建出一个ShadowTree,由一个个ShadowNode组成的树,最终会映射到原生的View。

Demo

和TurboModule一样,我们也先按照官网流程生成一个自定义的Fabric组件,然后后面我们在JS中去使用它,最终来看看它的构建流程。

我们定义一个JS文件,然后使用使用Codegen生成脚手架。

// @flow strict-localimport type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';type NativeProps = $ReadOnly<{|...ViewProps,text: ?string,// add other props here
|}>;export default (codegenNativeComponent<NativeProps>(
'RTNTestView',
): HostComponent<NativeProps>);

生成的代码如下:
在这里插入图片描述

入口

我们从index.js开始看,我们会省略一些React的逻辑,主要是看一下RN在Android上的流程,而对React对DOM对构建和管理不是我们所关心的。

1.1 index.js
入口调用了AppRegistry.registerComponent,() => App是我们写UI布局的地方,我们知道RN使用了JSX语法,我们 这样的UI标签本质其实是React.createElement(“Text”,…),所以我们是可以通过这个函数获取ReactElement的,这一部分的构建属于React的,我们不深入研究。

AppRegistry.registerComponent(appName, () => App);

1.2 AppRegistry.registerComponent
主要是调用了renderApplication

registerComponent(appKey: string,componentProvider: ComponentProvider,section?: boolean,
): string {const scopedPerformanceLogger = createPerformanceLogger();runnables[appKey] = (appParameters, displayMode) => {const concurrentRootEnabled = Boolean(appParameters.initialProps?.concurrentRoot ||appParameters.concurrentRoot,);// 详见1.3renderApplication(componentProviderInstrumentationHook(componentProvider,scopedPerformanceLogger,),appParameters.initialProps,appParameters.rootTag,wrapperComponentProvider && wrapperComponentProvider(appParameters),rootViewStyleProvider && rootViewStyleProvider(appParameters),appParameters.fabric,scopedPerformanceLogger,appKey === 'LogBox', // is logboxappKey,displayMode,concurrentRootEnabled,);};if (section) {sections[appKey] = runnables[appKey];}return appKey;
}

1.3 renderApplication

export default function renderApplication<Props: Object>(RootComponent: React.ComponentType<Props>,initialProps: Props,rootTag: any,WrapperComponent?: ?React.ComponentType<any>,rootViewStyle?: ?ViewStyleProp,fabric?: boolean,scopedPerformanceLogger?: IPerformanceLogger,isLogBox?: boolean,debugName?: string,displayMode?: ?DisplayModeType,useConcurrentRoot?: boolean,useOffscreen?: boolean,) {
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);const performanceLogger = scopedPerformanceLogger ?? GlobalPerformanceLogger;// 这里将传入的RootComponent外面包了两层
let renderable: React.MixedElement = (<PerformanceLoggerContext.Provider value={performanceLogger}><AppContainerrootTag={rootTag}fabric={fabric}WrapperComponent={WrapperComponent}rootViewStyle={rootViewStyle}initialProps={initialProps ?? Object.freeze({})}internal_excludeLogBox={isLogBox}><RootComponent {...initialProps} rootTag={rootTag} /></AppContainer></PerformanceLoggerContext.Provider>
);// ...debug相关操作if (useOffscreen && displayMode != null) {// $FlowFixMe[incompatible-type]// $FlowFixMe[prop-missing]const Activity: ActivityType = React.unstable_Activity;renderable = (<Activitymode={displayMode === DisplayMode.VISIBLE ? 'visible' : 'hidden'}>{renderable}</Activity>);}// We want to have concurrentRoot always enabled when you're on Fabric.const useConcurrentRootOverride = fabric;// ... 性能日志// 渲染element,renderable是我们在app.js定义的UI并且在外面套上AppContainer和PerformanceLoggerContext.Provider生成的Element树的根节点。// 详见1.4Renderer.renderElement({element: renderable,rootTag,useFabric: Boolean(fabric),useConcurrentRoot: Boolean(useConcurrentRootOverride),});// ... 性能日志
}

1.4 renderElement
这里就是判断一下使用新渲染器Fabric还是以前的渲染器,我们这里跟Fabric的。
ReactFabric会根据是否是dev(debug)来决定使用ReactFabric-dev还是ReactFabric-prod,区别就是dev会有一些调试能力。
我们来看ReactFabric-prod里的render。

export function renderElement({element,rootTag,useFabric,useConcurrentRoot,}: {element: React.MixedElement,rootTag: number,useFabric: boolean,useConcurrentRoot: boolean,}): void {if (useFabric) {// 详见1.5require('../Renderer/shims/ReactFabric').render(element,rootTag,null,useConcurrentRoot,{onCaughtError,onUncaughtError,onRecoverableError,},);} else {require('../Renderer/shims/ReactNative').render(element,rootTag,undefined,{onCaughtError,onUncaughtError,onRecoverableError,},);}
}

1.5 render
这个文件里逻辑也可以不用太关心,这里主要是处理树的数据结构构造和更新,加上源码里一堆魔数,看不懂~,主要看一下流程。
接下来我们直接跳到这个文件里创建ShadowNode的地方。

exports.render = function (element,containerTag,callback,concurrentRoot,options) {// roots是一个Map缓存var root = roots.get(containerTag);// 如果之前没有这个root节点,需要新建if (!root) {root = nativeOnUncaughtError;var onCaughtError = nativeOnCaughtError,onRecoverableError = defaultOnRecoverableError;options &&void 0 !== options.onUncaughtError &&(root = options.onUncaughtError);options &&void 0 !== options.onCaughtError &&(onCaughtError = options.onCaughtError);options &&void 0 !== options.onRecoverableError &&(onRecoverableError = options.onRecoverableError);concurrentRoot = concurrentRoot ? 1 : 0;// 新建Fiber root节点options = new FiberRootNode(containerTag,concurrentRoot,!1,"",root,onCaughtError,onRecoverableError,null);// 新建Fiber节点concurrentRoot = createFiber(3, null, null, 1 === concurrentRoot ? 1 : 0);options.current = concurrentRoot;concurrentRoot.stateNode = options;root = createCache();root.refCount++;options.pooledCache = root;root.refCount++;concurrentRoot.memoizedState = {element: null,isDehydrated: !1,cache: root};initializeUpdateQueue(concurrentRoot);root = options;roots.set(containerTag, root);}// 触发更新updateContainer(element, root, null, callback);a: if (((element = root.current), element.child))switch (element.child.tag) {case 27:case 5:element = getPublicInstance(element.child.stateNode);break a;default:element = element.child.stateNode;}else element = null;return element;
};

1.6 completeWork
调用createNode来创建Shadow节点,uiViewClassName则是我们最早在JS中声明的,以我们自定义的Fabric组件为例就是RTNTestView
这里的createNode是nativeFabricUIManager里的createNode,而nativeFabricUIManager是一个native对象,通过JSI加载进来的,加载的代码在UIManagerBinding.cpp中,看过之前TurboModule加载和JSI的章节的话,很容易可以看懂这里的逻辑,详见1.6.1

function completeWork(current, workInProgress, renderLanes) {// ...// 详见1.7oldProps = createNode(current,renderLanes.uiViewClassName,oldProps,updatePayload,workInProgress);// ...
}nativeFabricUIManage = nativeFabricUIManager,
createNode = _nativeFabricUIManage.createNode,

1.6.1 UIManagerBinding::createAndInstallIfNeeded
这里就是将JS nativeFabricUIManager和UIManagerBinding关联,所以我们要来看一下UIManagerBinding的get方法,看一下createNode是怎么定义的。

void UIManagerBinding::createAndInstallIfNeeded(jsi::Runtime& runtime,const std::shared_ptr<UIManager>& uiManager) {auto uiManagerModuleName = "nativeFabricUIManager";auto uiManagerValue =runtime.global().getProperty(runtime, uiManagerModuleName);// 如果之前已经设置过了不重复设置if (uiManagerValue.isUndefined()) {auto uiManagerBinding = std::make_shared<UIManagerBinding>(uiManager);auto object = jsi::Object::createFromHostObject(runtime, uiManagerBinding);// 通过JSI接口设置全局global的属性nativeFabricUIManager为native的uiManagerBindingruntime.global().setProperty(runtime, uiManagerModuleName, std::move(object));}
}

1.7 UIManagerBinding::get
createNode调用了uiManager->createNode来构造ShadowNode,然后把ShadowNode封装到JSI::Vaule返回给JS层。
这里的uiManager是一个UIManager实例,UIManagerBinding和UIManager是在FabricUIManager模块加载时,加载SurfaceManager,里面构造Schudler的时候进行绑定的。

jsi::Value UIManagerBinding::get(jsi::Runtime& runtime,const jsi::PropNameID& name) {auto methodName = name.utf8(runtime);UIManager* uiManager = uiManager_.get();if (methodName == "createNode") {auto paramCount = 5;return jsi::Function::createFromHostFunction(runtime,name,paramCount,[uiManager, methodName, paramCount](jsi::Runtime& runtime,const jsi::Value& /*thisValue*/,const jsi::Value* arguments,size_t count) -> jsi::Value {try {validateArgumentCount(runtime, methodName, paramCount, count);auto instanceHandle =instanceHandleFromValue(runtime, arguments[4], arguments[0]);if (!instanceHandle) {react_native_assert(false);return jsi::Value::undefined();}// 把构造的ShadowNode封装成JSI::Vaule以便返回给JS层return valueFromShadowNode(runtime,// 调用的uiManager->createNode来构造ShadowNode,详见1.8uiManager->createNode(tagFromValue(arguments[0]),stringFromValue(runtime, arguments[1]),surfaceIdFromValue(runtime, arguments[2]),RawProps(runtime, arguments[3]),std::move(instanceHandle)),true);} catch (const std::logic_error& ex) {LOG(FATAL) << "logic_error in createNode: " << ex.what();}});}// ...
}

1.8 UIManager::createNode
这里就是真正构建shadowNode的地方,回过头去看一下Codegen生成的脚手架,会发现这个方法里用到的好多类在刚刚都见过。
componentDescriptor,Props,State,ShadowNode。
后面我们结合脚手架生成的代码来看看这些类的作用。

std::shared_ptr<ShadowNode> UIManager::createNode(Tag tag,const std::string& name,SurfaceId surfaceId,RawProps rawProps,InstanceHandle::Shared instanceHandle) const {SystraceSection s("UIManager::createNode", "componentName", name);// 我们每一个Fabric控件都会有一个ComponentDescriptor,然后会根据组件的名称注册到componentDescriptorRegistry_中去// 这里我们就可以通过组件名称获取ComponentDescriptor  // 我们一会会介绍,根据我们自定义的组件来看的话,这里最终获取的是RTNTestViewComponentDescriptorauto& componentDescriptor = componentDescriptorRegistry_->at(name);auto fallbackDescriptor =componentDescriptorRegistry_->getFallbackComponentDescriptor();PropsParserContext propsParserContext{surfaceId, *contextContainer_.get()};auto family = componentDescriptor.createFamily({tag, surfaceId, std::move(instanceHandle)});const auto props = componentDescriptor.cloneProps(propsParserContext, nullptr, std::move(rawProps));const auto state = componentDescriptor.createInitialState(props, family);// 详见3.1auto shadowNode = componentDescriptor.createShadowNode(ShadowNodeFragment{.props = fallbackDescriptor != nullptr &&fallbackDescriptor->getComponentHandle() ==componentDescriptor.getComponentHandle()? componentDescriptor.cloneProps(propsParserContext,props,RawProps(folly::dynamic::object("name", name))): props,.children = ShadowNodeFragment::childrenPlaceholder(),.state = state,},family);if (delegate_ != nullptr) {// 详见3.2delegate_->uiManagerDidCreateShadowNode(*shadowNode);}if (leakChecker_) {leakChecker_->uiManagerDidCreateShadowNodeFamily(family);}return shadowNode;
}

脚手架代码

我们先来看一下脚手架生成的代码,一会接着上面createNode。

我们先看看注册ComponentDescriptor的流程,在第一章启动流程中,介绍过application会调用getDefaultReactHost构造ReactHost。

2.1 getDefaultReactHost
我们这里只关注注册ComponentDescriptor流程,其他的之前介绍启动流程的时候介绍过了。
这里调用DefaultComponentsRegistry.register

@OptIn(UnstableReactNativeAPI::class)
@JvmStatic
public fun getDefaultReactHost(context: Context,packageList: List<ReactPackage>,jsMainModulePath: String = "index",jsBundleAssetPath: String = "index",isHermesEnabled: Boolean = true,useDevSupport: Boolean = ReactBuildConfig.DEBUG,cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage> = emptyList(),
): ReactHost {//...val componentFactory = ComponentFactory()// 详见2.2DefaultComponentsRegistry.register(componentFactory)// ...
}

2.2 DefaultComponentsRegistry.register
构造DefaultComponentsRegistry

public fun register(componentFactory: ComponentFactory): DefaultComponentsRegistry =DefaultComponentsRegistry(componentFactory)

2.3 DefaultComponentsRegistry
构造的时候会调用initHybrid,initHybrid是一个native方法。

private val mHybridData: HybridData = initHybrid(componentFactory)

@DoNotStrip private external fun initHybrid(componentFactory: ComponentFactory): HybridData

2.4 initHybrid

jni::local_ref<DefaultComponentsRegistry::jhybriddata>
DefaultComponentsRegistry::initHybrid(jni::alias_ref<jclass>,ComponentFactory* delegate) {auto instance = makeCxxInstance(delegate);auto buildRegistryFunction =[](const EventDispatcher::Weak& eventDispatcher,const ContextContainer::Shared& contextContainer)-> ComponentDescriptorRegistry::Shared {ComponentDescriptorParameters params{.eventDispatcher = eventDispatcher,.contextContainer = contextContainer,.flavor = nullptr};// buildRegistryFunction会调用sharedProviderRegistry,详见2.5auto registry = DefaultComponentsRegistry::sharedProviderRegistry()->createComponentDescriptorRegistry(params);auto& mutableRegistry = const_cast<ComponentDescriptorRegistry&>(*registry);mutableRegistry.setFallbackComponentDescriptor(std::make_shared<UnimplementedNativeViewComponentDescriptor>(params));return registry;};delegate->buildRegistryFunction = buildRegistryFunction;return instance;
}

2.5 buildRegistryFunction

std::shared_ptr<const ComponentDescriptorProviderRegistry>DefaultComponentsRegistry::sharedProviderRegistry() {// 这里注册的是RN的核心模块auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();// 这里注册我们自定义的模块,详见2.6 if (DefaultComponentsRegistry::registerComponentDescriptorsFromEntryPoint) {(DefaultComponentsRegistry::registerComponentDescriptorsFromEntryPoint)(providerRegistry);} else {LOG(WARNING)<< "Custom component descriptors were not configured from JNI_OnLoad";}return providerRegistry;
}

2.6 DefaultComponentsRegistry::registerComponentDescriptorsFromEntryPoint
这里的registerComponentDescriptorsFromEntryPoint是facebook::react::registerComponents

void registerComponents(std::shared_ptr<const ComponentDescriptorProviderRegistry> registry) {// ...// 详见2.7autolinking_registerProviders(registry);
}

2.7 autolinking_registerProviders
这里调用add来注册ComponentDescriptor
RTNTestViewComponentDescriptor是脚手架生成的,concreteComponentDescriptorProvider是一个模板方法,其中生成ComponentDescriptorProvider中constructor可以构造RTNTestViewComponentDescriptor

void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {providerRegistry->add(concreteComponentDescriptorProvider<RTNTestViewComponentDescriptor>());return;
}

2.8 ComponentDescriptorRegistry::add
这里调用constructor,会构造RTNTestViewComponentDescriptor,然后根据名字和句柄分别存储在两个map中。

void ComponentDescriptorRegistry::add(ComponentDescriptorProvider componentDescriptorProvider) const {std::unique_lock lock(mutex_);auto componentDescriptor = componentDescriptorProvider.constructor({parameters_.eventDispatcher,parameters_.contextContainer,componentDescriptorProvider.flavor});// ...auto sharedComponentDescriptor = std::shared_ptr<const ComponentDescriptor>(std::move(componentDescriptor));_registryByHandle[componentDescriptorProvider.handle] =sharedComponentDescriptor;_registryByName[componentDescriptorProvider.name] = sharedComponentDescriptor;
}

2.9 RTNTestViewComponentDescriptor
RTNTestViewComponentDescriptor是一个ConcreteComponentDescriptor,ConcreteComponentDescriptor封装了组件的信息,包括shadowNode,state,prop等。
而RTNTestViewShadowNode则是一个ConcreteViewShadowNode模板类,里面会包含所RTNTestViewProps,RTNTestViewEventEmitter,RTNTestViewState,这样脚手架就实现了一个自定义Fabric组件然后关联组件自己的Props,State,EventEmitter。

using RTNTestViewComponentDescriptor = ConcreteComponentDescriptor<RTNTestViewShadowNode>;using RTNTestViewShadowNode = ConcreteViewShadowNode<RTNTestViewComponentName,RTNTestViewProps,RTNTestViewEventEmitter,RTNTestViewState>;

接着第一节创建ShadowNode

3.1 RTNTestViewComponentDescriptor.createShadowNode
构建了一个RTNTestViewShadowNode

std::shared_ptr<ShadowNode> createShadowNode(const ShadowNodeFragment& fragment,const ShadowNodeFamily::Shared& family) const override {// ShadowNodeT是模板参数,RTNTestViewShadowNode,所以这里构建了一个RTNTestViewShadowNodeauto shadowNode =std::make_shared<ShadowNodeT>(fragment, family, getTraits());adopt(*shadowNode);return shadowNode;
}

3.2 uiManagerDidCreateShadowNode
这里也是接着1.8的,从delegate_->uiManagerDidCreateShadowNode过来。

void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode& shadowNode) {if (delegate_ != nullptr) {// 详见3.3delegate_->schedulerDidRequestPreliminaryViewAllocation(shadowNode);}
}

3.3 FabricUIManagerBinding::schedulerDidRequestPreliminaryViewAllocation
调用了mountingManager调用maybePreallocateShadowNode,构造好的shadowNode后续需要mount挂载,挂载之后就会构建原生的View。

void FabricUIManagerBinding::schedulerDidRequestPreliminaryViewAllocation(const ShadowNode& shadowNode) {auto mountingManager = getMountingManager("preallocateView");if (!mountingManager) {return;}// 详见3.4 mountingManager->maybePreallocateShadowNode(shadowNode);// ...
}

3.4 FabricMountingManager::maybePreallocateShadowNode
这里构建了ShadowView,然后调用了preallocateShadowView

void FabricMountingManager::maybePreallocateShadowNode(const ShadowNode& shadowNode) {if (!shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) {return;}static thread_local bool onMainThread = isOnMainThread();if (onMainThread) {// View preallocation is not beneficial when rendering on the main threadreturn;}// 构造ShadowViewauto shadowView = ShadowView(shadowNode);if (ReactNativeFeatureFlags::useOptimisedViewPreallocationOnAndroid()) {std::lock_guard lock(preallocateMutex_);preallocatedViewsQueue_.push_back(std::move(shadowView));} else {// 详见3.5preallocateShadowView(shadowView);}
}

3.5 FabricMountingManager::preallocateShadowView
每个surfaceId会映射一个队列,队列里会记录所有使用过的View的TAG,然后调用preallocateView,preallocateView映射java层的方法。

void FabricMountingManager::preallocateShadowView(const ShadowView& shadowView) {SystraceSection section("FabricMountingManager::preallocateShadowView");{// 每个SurfaceId会映射一个数组,数组里记录使用到的View的TAGstd::lock_guard lock(allocatedViewsMutex_);auto allocatedViewsIterator =allocatedViewRegistry_.find(shadowView.surfaceId);if (allocatedViewsIterator == allocatedViewRegistry_.end()) {return;}auto& allocatedViews = allocatedViewsIterator->second;if (allocatedViews.find(shadowView.tag) != allocatedViews.end()) {return;}allocatedViews.insert(shadowView.tag);}bool isLayoutableShadowNode = shadowView.layoutMetrics != EmptyLayoutMetrics;// 映射java的方法,preallocateViewstatic auto preallocateView =JFabricUIManager::javaClassStatic()->getMethod<void(jint, jint, jstring, jobject, jobject, jboolean)>("preallocateView");// ...处理Prop和Stateauto component = getPlatformComponentName(shadowView);// 调用preallocateView,这里会调用到java的preallocateView,详见3.6 preallocateView(javaUIManager_,shadowView.surfaceId,shadowView.tag,component.get(),props.get(),(javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr),isLayoutableShadowNode);
}

3.6 SurfaceMountingManager.preallocateView
调用createViewUnsafe构建View

@UiThread
public void preallocateView(@NonNull String componentName,int reactTag,@Nullable ReadableMap props,@Nullable StateWrapper stateWrapper,boolean isLayoutable) {UiThreadUtil.assertOnUiThread();if (isStopped()) {return;}if (getNullableViewState(reactTag) != null) {return;}// 详见3.7createViewUnsafe(componentName, reactTag, props, stateWrapper, null, isLayoutable);
}

3.7 createViewUnsafe

@UiThread
public void createViewUnsafe(@NonNull String componentName,int reactTag,@Nullable ReadableMap props,@Nullable StateWrapper stateWrapper,@Nullable EventEmitterWrapper eventEmitterWrapper,boolean isLayoutable) {Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,"SurfaceMountingManager::createViewUnsafe(" + componentName + ")");try {ReactStylesDiffMap propMap = new ReactStylesDiffMap(props);// 将View的核心要素Prop,State,EventEmitter封装到ViewStateViewState viewState = new ViewState(reactTag);viewState.mCurrentProps = propMap;viewState.mStateWrapper = stateWrapper;viewState.mEventEmitter = eventEmitterWrapper;mTagToViewState.put(reactTag, viewState);if (isLayoutable) {ViewManager viewManager = mViewManagerRegistry.get(componentName);// 调用ViewManager.createView,这里的ViewManager每个Fabric组件都有一个// 以我们实现的RTNTestViewManager为例来看看,详见3.8viewState.mView =viewManager.createView(reactTag, mThemedReactContext, propMap, stateWrapper, mJSResponderHandler);viewState.mViewManager = viewManager;}} finally {Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);}
}

3.8 RTNTestViewManager
这里我们实现的RTNTestViewManager继承自SimpleViewManager,而SimpleViewManager继承自BaseViewManager,BaseViewManager继承自ViewManager。
ViewManager的createView会调用createViewInstance,同时createView会更新Prop,State,EventEmitter。
而这里createViewInstance就会构建View。

@ReactModule(name = RTNTestViewManager.NAME)
public class RTNTestViewManager extends SimpleViewManager<TextView>implements RTNTestViewManagerInterface<TextView> {private final ViewManagerDelegate<TextView> mDelegate;static final String NAME = "RTNTestView";public RTNTestViewManager(ReactApplicationContext context) {mDelegate = new RTNTestViewManagerDelegate<>(this);}@Nullable@Overrideprotected ViewManagerDelegate<TextView> getDelegate() {return mDelegate;}@NonNull@Overridepublic String getName() {return RTNTestViewManager.NAME;}@NonNull@Overrideprotected TextView createViewInstance(@NonNull ThemedReactContext context) {// 返回自定义的View,这里只是随便返回一个TextView,实际是要返回一个自定义的Viewreturn new TextView(context);}@Override@ReactProp(name = "text")public void setText(TextView view, @Nullable String text) {view.setText(text);}
}

其他

  • Props
    Props继承了ViewProps,之所以要有继承是因为需要实现一些基础的属性,ViewProps本质是BaseViewProps,BaseViewProps继承了YogaStylableProps和AccessibilityProps,实现基础属性,比如有宽度高度等等基础属性,这样我们自定义的Fabric就只需要定义我们额外的属性。

    class RTNTestViewProps final : public ViewProps {
    public:
    RTNTestViewProps() = default;
    RTNTestViewProps(const PropsParserContext& context, const RTNTestViewProps &sourceProps, const RawProps &rawProps);

      #pragma mark - Propsstd::string text{};
    

    };

小结

本节我们只是介绍了Fabric的组件以及渲染器的核心流程,在JS层定义UI布局,然后React层会构建一个DOM树,后面会将DOM树同步到ShadowNode层,最后会根据ShadowNode构建原生组件。
EmitterEvent,Props,State这些分发设置我们就不继续深入研究了,如果后续有机会的话可能会更加深入学习,目前我们只是了解一下跨端开发的原理和设计思想,拓宽一下眼界,下一节我们会学习一下Flutter,Flutter和RN的设计思想完全不同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/460336.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推荐一款优秀的pdf编辑器:Ashampoo PDF Pro

Ashampoo PDF Pro是管理和编辑 PDF 文档的完整解决方案。程序拥有您创建、转换、编辑和保护文档所需的一切功能。根据需要可以创建特定大小的文档&#xff0c;跨设备可读&#xff0c;还可以保护文件。现在您还能像编辑Word文档一样编辑PDF! 软件特点 轻松处理文字 如 Microso…

开发流程初学者指南——需求分析

目录 从零开始理解需求分析什么是需求分析&#xff1f;需求分析的目标需求分析的基本原则需求分析的各个阶段需求分析的常用方法和工具编写需求文档总结 从零开始理解需求分析 需求分析是软件开发过程中不可或缺的一环&#xff0c;它帮助我们明确用户的需求&#xff0c;确保最…

Linux中Web服务器配置和管理(Apache)

文章目录 一、WEB服务器介绍1.1、WEB服务器概述1.2、WEB服务器的发展历史1.3、WEB服务器的优点与缺点1.4、WEB服务器的工作流程 二、Apache介绍2.1、Apache是什么2.2、Apache的发展史与应用场景2.3、Apache的特点2.4、Apache的工作原理2.5、Apache的模块 三、安装使用Apache服务…

在VS Code中操作MySQL数据库

【基础篇】 【小白专用24.5.26 已验证】VSCode下载和安装与配置PHP开发环境&#xff08;详细版&#xff09;_vscode php-CSDN博客 ~~~~~~~~~~~~~~~~~~~~~~~~~ 在VS Code中下载插件 Prettier SQL VSCode 和 MySQL : 随后在VS Code中点击Database图标 在连接界面输入MySQL数据库…

Unity可视化Shader工具ASE介绍——自定义函数

阿赵的Unity可视化Shader工具ASE介绍目录   大家好&#xff0c;我是阿赵。   之前介绍过一些ASE的用法&#xff0c;发现漏了一个比较重要的&#xff0c;自定义函数的使用。这里补充一下。 一、 使用的场合 在使用ASE制作Shader的过程中&#xff0c;可能会遇到以下这些情况…

聊聊我在新加坡的近况

我是 2022 年 4 月初过来新加坡的&#xff0c;然后两个月后就把老婆孩子们也接了过来。时至今日&#xff0c;已经两年半有余了。 22 年 8 月初的时候&#xff0c;写过一篇文章「聊聊我在新加坡的生活和工作体验」&#xff0c;没想到成了一篇热门文章&#xff0c;在知乎上不知不…

贪心算法入门(一)

1.什么是贪心算法&#xff1f; 贪心算法是一种解决问题的策略&#xff0c;它将复杂的问题分解为若干个步骤&#xff0c;并在每一步都选择当前最优的解决方案&#xff0c;最终希望能得到全局最优解。这种策略的核心在于“最优”二字&#xff0c;意味着我们追求的是以最少的时间和…

深度强化学习-学习笔记

1.PPO &#xff08;1&#xff09;DeepMind公司发明的算法PPO &#xff08;2&#xff09;OpenAI公司发明的算法PPO2&#xff1b; 近端策略优化算法&#xff08;proximal policy optimization&#xff0c;PPO&#xff09; Actor 网络输出在给定状态 S下采取每个动作的概率分布&a…

pytest脚本常用的执行命令

pytest脚本常用的执行命令 一、一般执行的脚本&#xff0c;执行.py文件整个脚本二、执行.py文件脚本中的一个模块三、执行脚本&#xff0c;执行.py文件整个脚本&#xff0c;或则一个模块&#xff0c;查看对应的日志信息3.1.py文件执行allure的脚本3.2去dos框下去执行对应的脚本…

【水果数据集】水果品种识别 人工智能 机器学习(含数据集)

一、背景意义 随着智能农业和食品管理系统的发展&#xff0c;准确识别不同水果种类变得尤为重要。水果种类数据集是针对水果识别与分类的深度学习项目而建立的重要资源。通过自动化的水果识别系统&#xff0c;农场主能够实时监控水果的种类和成熟度&#xff0c;从而优化采摘和销…

Android平台RTSP|RTMP播放器高效率如何回调YUV或RGB数据?

技术背景 我们在做Android平台RTSP、RTMP播放器的时候&#xff0c;经常遇到这样的技术诉求&#xff0c;开发者希望拿到播放器解码后的YUV或RGB数据&#xff0c;投递给视觉算法&#xff0c;做AI分析&#xff0c;本文以ffmpeg和大牛直播SDK的SmartPlayer为例&#xff0c;介绍下相…

计算机网络-MSTP概述

一、RSTP/STP的缺陷与不足 前面我们学习了RSTP对于STP的一些优化与快速收敛机制。但在划分VLAN的网络中运行RSTP/STP&#xff0c;局域网内所有的VLAN共享一棵生成树&#xff0c;被阻塞后的链路将不承载任何流量&#xff0c;无法在VLAN间实现数据流量的负载均衡&#xff0c;导致…

ios 快捷指令扩展(Intents Extension)简单使用 swift语言

本文介绍使用Xcode15 建立快捷指令的Extension&#xff0c;并描述如何修改快捷指令的IntentHandler&#xff0c;带参数跳转主应用&#xff1b;以及展示多个选项的快捷指令弹框(配置intentdefinition文件)&#xff0c;点击选项带参数跳到主应用的方法 创建快捷指令 快捷指令是…

智能财务 | 数据与融合,激发企业财务数智化转型思考

数据与融合&#xff0c;激发企业财务数智化转型思考 用友持续深耕企业财务领域&#xff0c;见证中国企业走过了财务电算化、信息化时代&#xff0c;当下共同经历数智化时代。2023 年度&#xff0c;通过走访标杆企业&#xff0c;与高校教授、权威机构学者共同探讨等形式&#xf…

openpnp - 解决“底部相机高级校正成功后, 开机归零时,吸嘴自动校验失败的问题“

文章目录 openpnp - 解决"底部相机高级校正成功后, 开机归零时&#xff0c;吸嘴自动校验失败的问题"概述笔记问题现象1问题现象2原因分析现在底部相机和吸嘴的位置偏差记录修正底部相机位置现在再看看NT1在底部相机中的位置开机归零&#xff0c;看看是否能通过所有校…

DreamClear:中科院与字节联合推出!隐私安全优先的高性能图像修复技术

❤️ 如果你也关注大模型与 AI 的发展现状&#xff0c;且对大模型应用开发非常感兴趣&#xff0c;我会快速跟你分享最新的感兴趣的 AI 应用和热点信息&#xff0c;也会不定期分享自己的想法和开源实例&#xff0c;欢迎关注我哦&#xff01; &#x1f966; 微信公众号&#xff…

SpringBoot驱动的毕业生招聘信息平台

1 系统概述 1.1 概述  随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。人们的生活水平不断提高&#xff0c;日常生活中毕业生对招聘平台方面的要求也在不断提高&#xff0c;需要招聘平台的人数更是不断增加&#xff0c;使得毕业生信息招聘平台的开发成为必需而且…

Mac程序坞窗口预览的方法来了

当你同一程序内打开的窗口过多的时候&#xff0c;在Mac上想要切换就只能打开程序然后在内部进行切换&#xff0c;没办法直达你想要打开的窗口&#xff0c;多了一步的操作&#xff0c;那么如何才能一步到位呢 如果程序坞有应用程序的缩略图&#xff0c;是不是就可以一步到位了&…

【C/C++】结构体的定义

零.导言 在上一篇博客中&#xff0c;我讲解了qsort函数&#xff0c;并在其中提到了结构体数组的排序&#xff0c;那么结构体是什么呢? 接下来我将详细讲解结构体的定义。 一&#xff0c;结构体是什么&#xff1f; 结构体是自定义的数据类型&#xff0c;可以存放自定义的数据。…

JavaScript 中如何识别异步函数?

我们如何判断一个函数是否是异步函数&#xff08;async function&#xff09;呢&#xff1f; 遇到问题的思考过程是什么呢&#xff0c;首先需要找到二者的区别&#xff0c;那就打印看一下&#xff0c;然后在思考如何做。 由此可以看出二者的差异。 1、使用 typeof 检查函数类…