做者:少林寺扫地的css
本文分析的源码版本:node
"react": "16.3.1",
"react-native": "0.55.3",
复制代码
有个好方法,经过打断点来打印createInstance和render的调用栈,从而帮助咱们分析出RN建立view的过程。react
先看createInstance和render的调用栈与时序图:android
function performUnitOfWork(workInProgress) {
// The current, flushed, state of this fiber is the alternate.
// Ideally nothing should rely on this, but relying on it here
// means that we don't need an additional field on the work in // progress. var current = workInProgress.alternate; // See if beginning this work spawns more work. startWorkTimer(workInProgress); { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) { stashedWorkInProgressProperties = assignFiberPropertiesInDEV( stashedWorkInProgressProperties, workInProgress ); } var next = beginWork(current, workInProgress, nextRenderExpirationTime); { ReactDebugCurrentFiber.resetCurrentFiber(); if (isReplayingFailedUnitOfWork) { // Currently replaying a failed unit of work. This should be unreachable, // because the render phase is meant to be idempotent, and it should // have thrown again. Since it didn't, rethrow the original error, so
// React's internal stack is not misaligned. rethrowOriginalError(); } } if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); } if (next === null) { // If this doesn't spawn new work, complete the current work.
next = completeUnitOfWork(workInProgress);
}
ReactCurrentOwner.current = null;
return next;
}
复制代码
从这里咱们能够看到,调用beginWork()函数获取next节点,next就是下一个view容器,这个函数其实就是父render遍历view树的过程,过程当中还会执行每个子view的render。git
再看调用performUnitOfWork的函数workLoop就一目了然了,先看源码:程序员
function workLoop(isAsync) {
if (!isAsync) {
// Flush all expired work.
while (nextUnitOfWork !== null) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
} else {
// Flush asynchronous work until the deadline runs out of time.
while (nextUnitOfWork !== null && !shouldYield()) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
}
复制代码
在performUnitOfWork返回next不为空的时候,执行while循环,就是遍历view树的过程,也就是render的遍历过程了!github
再看completeUnitOfWork的调用源码:json
if (next === null) {
// If this doesn't spawn new work, complete the current work. next = completeUnitOfWork(workInProgress); } 复制代码
当next===null时,也就是一个父view下的全部子view都已经遍历过的时候,此时全部view信息都保存在workInProgress中,执行completeUnitOfWork(workInProgress),会再执行一个循环,若是这些view没有被建立原生view的话,就会调用到createInstance建立原生view了(具体源码能够本身看下,比较简单)小程序
至此,程序加载页面时,是如何调用到各个view的render,以及是如何调用createInstance的过程就已经清晰了。react-native
接下来,以最基础的View组件为例,分析基础组件render的具体过程。
node_modules/react-native/Libraries/Components/View/View.js
class View extends ReactNative.NativeComponent<Props> {
static propTypes = ViewPropTypes;
static childContextTypes = ViewContextTypes;
viewConfig = {
uiViewClassName: 'RCTView',
validAttributes: ReactNativeViewAttributes.RCTView,
};
getChildContext(): ViewChildContext {
return {
isInAParentText: false,
};
}
render() {
invariant(
!(this.context.isInAParentText && Platform.OS === 'android'),
'Nesting of <View> within <Text> is not supported on Android.',
);
// WARNING: This method will not be used in production mode as in that mode we
// replace wrapper component View with generated native wrapper RCTView. Avoid
// adding functionality this component that you'd want to be available in both // dev and prod modes. return <RCTView {...this.props} />; } } const RCTView = requireNativeComponent('RCTView', View, { nativeOnly: { nativeBackgroundAndroid: true, nativeForegroundAndroid: true, }, }); 复制代码
截取部分源码,能够看到viewConfig中有个uiViewClassName: 'RCTView',这个RCTView就是View控件在原生代码中映射的控件类名。 render返回的RCTView是经过requireNativeComponent生成的,再看requireNativeComponent node_modules/react-native/Libraries/ReactNative/requireNativeComponent.js 源码:
return createReactNativeComponentClass(viewName, getViewConfig);
复制代码
这个部分最终返回上面函数的执行,因此再看看createReactNativeComponentClass node_modules/react-native/Libraries/Renderer/shims/createReactNativeComponentClass.js
'use strict';
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
module.exports =
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.createReactNativeComponentClass;
复制代码
能够看到createReactNativeComponentClass是在ReactNative中定义的 另外说下: __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 从字面理解估计是将来要废弃的方法。 再看: node_modules/react-native/Libraries/Renderer/shims/ReactNative.js
'use strict';
import type {ReactNativeType} from 'ReactNativeTypes';
let ReactNative;
if (__DEV__) {
ReactNative = require('ReactNativeRenderer-dev');
} else {
ReactNative = require('ReactNativeRenderer-prod');
}
module.exports = (ReactNative: ReactNativeType);
复制代码
咱们能够发现ReactNative原来就是ReactNativeRenderer-dev或ReactNativeRenderer-prod,区别是啥程序员都懂得,至于ReactNativeType,这是相似一个简化的接口,不作赘述了。
如今看ReactNativeRenderer-dev,看名字就知道这个类是Render的具体实现者代码行数将近一万五,只能截取着看了。
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
// Used as a mixin in many createClass-based components
NativeMethodsMixin: NativeMethodsMixin,
// Used by react-native-github/Libraries/ components
ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin, // requireNativeComponent
ReactNativeComponentTree: ReactNativeComponentTree, // ScrollResponder
ReactNativePropRegistry: ReactNativePropRegistry, // flattenStyle, Stylesheet
TouchHistoryMath: TouchHistoryMath, // PanResponder
createReactNativeComponentClass: createReactNativeComponentClass, // RCTText, RCTView, ReactNativeART
takeSnapshot: takeSnapshot
}
复制代码
先看这段,找到 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED下的createReactNativeComponentClass了,再看具体定义:
/**
* Creates a renderable ReactNative host component.
* Use this method for view configs that are loaded from UIManager.
* Use createReactNativeComponentClass() for view configs defined within JavaScript.
*
* @param {string} config iOS View configuration.
* @private
*/
var createReactNativeComponentClass = function(name, callback) {
return register(name, callback);
};
复制代码
再看register:
var viewConfigCallbacks = new Map();
var viewConfigs = new Map();
/**
* Registers a native view/component by name.
* A callback is provided to load the view config from UIManager.
* The callback is deferred until the view is actually rendered.
* This is done to avoid causing Prepack deopts.
*/
function register(name, callback) {
invariant(
!viewConfigCallbacks.has(name),
"Tried to register two views with the same name %s",
name
);
viewConfigCallbacks.set(name, callback);
return name;
}
复制代码
这样就把这个view注册到viewConfigCallbacks中去了,而viewConfigCallbacks是在下面函数中被使用的
/**
* Retrieves a config for the specified view.
* If this is the first time the view has been used,
* This configuration will be lazy-loaded from UIManager.
*/
function get$1(name) {
var viewConfig = void 0;
if (!viewConfigs.has(name)) {
var callback = viewConfigCallbacks.get(name);
invariant(
typeof callback === "function",
"View config not found for name %s",
name
);
viewConfigCallbacks.set(name, null);
viewConfig = callback();
viewConfigs.set(name, viewConfig);
} else {
viewConfig = viewConfigs.get(name);
}
invariant(viewConfig, "View config not found for name %s", name);
return viewConfig;
}
复制代码
get$1是在createInstance函数中被调用,也正是在这个函数中调用了UIManager.createView,UIManager.createView则是直接调用原生的接口函数来建立原生view的。
createInstance: function(
type,
props,
rootContainerInstance,
hostContext,
internalInstanceHandle
) {
var tag = ReactNativeTagHandles.allocateTag();
var viewConfig = get$1(type);
{
for (var key in viewConfig.validAttributes) {
if (props.hasOwnProperty(key)) {
deepFreezeAndThrowOnMutationInDev(props[key]);
}
}
}
var updatePayload = create(props, viewConfig.validAttributes);
UIManager.createView(
tag, // reactTag
viewConfig.uiViewClassName, // viewName
rootContainerInstance, // rootTag
updatePayload
);
var component = new ReactNativeFiberHostComponent(tag, viewConfig);
precacheFiberNode(internalInstanceHandle, tag);
updateFiberProps(tag, props);
// Not sure how to avoid this cast. Flow is okay if the component is defined
// in the same file but if it's external it can't see the types.
return component;
},
复制代码
函数的最后,生成了component返回给调用者使用。
另外,补充一个ReactNativeRenderer的主要变量的关系图,方便你们理解源码:
很是感谢阅读
》》》》》》》》》》》》》》》》》》》》》从这里断开了,字段长度的限制《《《《《《《《《《《《《《《《《《《《《《《
一下开始都是Java的代码了。 先给一个调用关系图:
@ReactMethod
public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
if (DEBUG) {
String message =
"(UIManager.createView) tag: " + tag + ", class: " + className + ", props: " + props;
FLog.d(ReactConstants.TAG, message);
PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.UI_MANAGER, message);
}
mUIImplementation.createView(tag, className, rootViewTag, props);
}
复制代码
注解@ReactMethod的意思就是这个函数会由JS代码调用
咱们能够看到个参数int tag, String className, int rootViewTag, ReadableMap props,正是JS代码中传入的参数 UIManager.createView( tag, // reactTag viewConfig.uiViewClassName, // viewName rootContainerInstance, // rootTag updatePayload ); 以后会调用UIImplementation的createView:
/**
* Invoked by React to create a new node with a given tag, class name and properties.
*/
public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
ReactShadowNode cssNode = createShadowNode(className);
ReactShadowNode rootNode = mShadowNodeRegistry.getNode(rootViewTag);
Assertions.assertNotNull(rootNode, "Root node with tag " + rootViewTag + " doesn't exist");
cssNode.setReactTag(tag);
cssNode.setViewClassName(className);
cssNode.setRootTag(rootNode.getReactTag());
cssNode.setThemedContext(rootNode.getThemedContext());
mShadowNodeRegistry.addNode(cssNode);
ReactStylesDiffMap styles = null;
if (props != null) {
styles = new ReactStylesDiffMap(props);
cssNode.updateProperties(styles);
}
handleCreateView(cssNode, rootViewTag, styles);
}
复制代码
cssNode,rootNode分别存放着,css样式相关信息,和view自己的相关信息,此时真正咱们view的style的相关属性经过props传入,经过 new ReactStylesDiffMap(props);生成一个map,也就是styles,而后经过cssNode.updateProperties(styles);设置给cssNode,过程以下,具体实如今ReactShadowNodeImpl的updateProperties:
@Override
public final void updateProperties(ReactStylesDiffMap props) {
ViewManagerPropertyUpdater.updateProps(this, props);
onAfterUpdateTransaction();
}
复制代码
再看ViewManagerPropertyUpdater.updateProps:
public static <T extends ReactShadowNode> void updateProps(T node, ReactStylesDiffMap props) {
ShadowNodeSetter<T> setter = findNodeSetter(node.getClass());
ReadableMap propMap = props.mBackingMap;
ReadableMapKeySetIterator iterator = propMap.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
setter.setProperty(node, key, props);
}
}
复制代码
while循环里,全部样式设置被一个一个从props取出,设置给以前的cssNode 这些执行后,会调用handleCreateView:
protected void handleCreateView(
ReactShadowNode cssNode,
int rootViewTag,
@Nullable ReactStylesDiffMap styles) {
if (!cssNode.isVirtual()) {
mNativeViewHierarchyOptimizer.handleCreateView(cssNode, cssNode.getThemedContext(), styles);
}
}
复制代码
再看NativeViewHierarchyOptimizer.handleCreateView:
/**
* Handles a createView call. May or may not actually create a native view.
*/
public void handleCreateView(
ReactShadowNode node,
ThemedReactContext themedContext,
@Nullable ReactStylesDiffMap initialProps) {
if (!ENABLED) {
int tag = node.getReactTag();
mUIViewOperationQueue.enqueueCreateView(
themedContext,
tag,
node.getViewClass(),
initialProps);
return;
}
复制代码
在这个会建立一个createview的任务,放入mUIViewOperationQueue队列中等待执行, 熟悉handler都懂得,不熟悉的请百度一下安卓handler,如今只讲最后的执行在CreateViewOperation的execute方法:
@Override
public void execute() {
Systrace.endAsyncFlow(Systrace.TRACE_TAG_REACT_VIEW, "createView", mTag);
mNativeViewHierarchyManager.createView(
mThemedContext,
mTag,
mClassName,
mInitialProps);
}
复制代码
再看mNativeViewHierarchyManager.createView:
public synchronized void createView(
ThemedReactContext themedContext,
int tag,
String className,
@Nullable ReactStylesDiffMap initialProps) {
UiThreadUtil.assertOnUiThread();
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_VIEW,
"NativeViewHierarchyManager_createView")
.arg("tag", tag)
.arg("className", className)
.flush();
try {
ViewManager viewManager = mViewManagers.get(className);
View view = viewManager.createView(themedContext, mJSResponderHandler);
mTagsToViews.put(tag, view);
mTagsToViewManagers.put(tag, viewManager);
// Use android View id field to store React tag. This is possible since we don't inflate // React views from layout xmls. Thus it is easier to just reuse that field instead of // creating another (potentially much more expensive) mapping from view to React tag view.setId(tag); if (initialProps != null) { viewManager.updateProperties(view, initialProps); } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_VIEW); } } 复制代码
这里最终找到了ViewManager经过createView建立view:
/**
* Creates a view and installs event emitters on it.
*/
public final T createView(
ThemedReactContext reactContext,
JSResponderHandler jsResponderHandler) {
T view = createViewInstance(reactContext);
addEventEmitters(reactContext, view);
if (view instanceof ReactInterceptingViewGroup) {
((ReactInterceptingViewGroup) view).setOnInterceptTouchEventListener(jsResponderHandler);
}
return view;
}
复制代码
这正的建立工做交给了createViewInstance,这是个虚函数,ViewManager是虚基类, 因此这正的建立工做交给了最终实现他的类来完成了,譬如ReactTextViewManager会 @Override public ReactTextView createViewInstance(ThemedReactContext context) { return new ReactTextView(context); } new一个ReactTextView实例出来,也就是说会根据JS那边具体组件名称(譬如View,Text,Image),来建立相应的native的View来! 这时就最终建立出原生VIew了!(IOS同理)
原文连接: tech.meicai.cn/detail/66, 也可微信搜索小程序「美菜产品技术团队」,干货满满且每周更新,想学习技术的你不要错过哦。