#React V18.2 源码
前置基础知识:工厂函数
工厂函数是一种设计模式,用于动态创建对象或函数实例。其核心思想是通过封装对象创建的细节,提供统一的接口,从而增强代码的灵活性和可维护性,有一些核心作用:
- 解耦创建逻辑:将对象的实例化过程与使用分离,调用方无需关心具体实现细节。
- 动态生成:根据输入参数返回不同类型的对象或函数。
- 统一接口:通过单一入口点管理多种创建场景。
工厂函数由构造函数进阶而来,都是用来创建实例。
这是一个函数,KindofNode“某类节点”的构造函数,可以通过new,创建一个实例对象(开辟一块空间,将原型拷贝到该空间,并将该空间的this指向该实例)。很好理解。
function KindOfNode(kind, properties1, properties2, properties3) {//这是一个构造函数this.kind = kind;this.properties1 = properties1;this.properties2 = properties2;this.properties3 = properties3;
}aNode = new KindOfNode(null, '属性1', '属性2', '属性3'); //创建一个对象console.dir(aNode);
//kindOfNode {
// kind: null,
// properties1: '属性1',
// properties2: '属性2',
// properties3: '属性3'
// }
create关键词函数,在框架中约定俗成为构造函数(博主观察发现),让我们对这个构造函数进行改进,可以发现可以选择性的对构造出来的实例某些属性进行赋值。
function createNode(kind, properties1, properties2, properties3) {//这是一个工厂函数const node = new KindOfNode(kind, properties1, properties2, properties3);if (kind === null || kind === undefined) {node.kind = 'node';}if (kind === 'hostFiber') {node.kind = 'hostFiber';}return node;
}fa_node = createNode('hostFiber', '属性1', '属性2', '属性3'); //创建一个对象
fb_node = createNode(null, '属性1', '属性2', '属性3'); //创建一个对象console.dir(fa_node);
console.dir(fb_node);
// kindOfNode {
// kind: 'hostFiber',
// properties1: '属性1',
// properties2: '属性2',
// properties3: '属性3'
// }
// kindOfNode {
// kind: 'node',
// properties1: '属性1',
// properties2: '属性2',
// properties3: '属性3'
// }
让我们正式进入createRoot()方法
相信有了前置知识,代码看起来不那么费力了。
在React源码中,构造函数的使用:通常以"create"关键字命名的函数,函数返回一个构造函数实例或者嵌套调用一个"create"关键字命名的函数进行嵌套调用。
CreateRoot方法比较复杂,我列举出了重要"create"关键词的五种。
这是createRoot过程中完整的函数调用栈,嵌套调用了多种函数:
其中:
- exports.createRoot是在node.js中遵循CommanJS标准的包导出。
- createRoot$1,出于安全性添加的一层。
- createRoot➡️createFiber,依此进行react初始化创建。
- FiberNode构造函数是push进栈最后一个栈帧,运行完后将从上到下依次pop出栈。
一、createRoot()
接收(container, options),返回对应React中虚拟DOM对象,其中:
- container:其中我们在index.html入口文件选择的真实DOM根节点。
- options:用于配置这个 React 根节点的对象。(基本用不到这个参数吧)
//createRoot()
function createRoot(container, options) {if (!isValidContainer(container)) {throw new Error('createRoot(...): Target container is not a DOM element.');}warnIfReactDOMContainerInDEV(container);var isStrictMode = false;var concurrentUpdatesByDefaultOverride = false;var identifierPrefix = '';var onRecoverableError = defaultOnRecoverableError;var transitionCallbacks = null;if (options !== null && options !== undefined) {{if (options.hydrate) {warn('hydrate through createRoot is deprecated. Use ReactDOMClient.hydrateRoot(container, <App />) instead.');} else {if (typeof options === 'object' && options !== null && options.$$typeof === REACT_ELEMENT_TYPE) {error('You passed a JSX element to createRoot. You probably meant to ' + 'call root.render instead. ' + 'Example usage:\n\n' + ' let root = createRoot(domContainer);\n' + ' root.render(<App />);');}}}if (options.unstable_strictMode === true) {isStrictMode = true;}if (options.identifierPrefix !== undefined) {identifierPrefix = options.identifierPrefix;}if (options.onRecoverableError !== undefined) {onRecoverableError = options.onRecoverableError;}if (options.transitionCallbacks !== undefined) {transitionCallbacks = options.transitionCallbacks;}}var root = createContainer(container, ConcurrentRoot, null, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError);markContainerAsRoot(root.current, container);var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;listenToAllSupportedEvents(rootContainerElement);return new ReactDOMRoot(root);
}
二、createContainer()
function createContainer(containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) {var hydrate = false;var initialChildren = null;return createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError);
}
三、createFiberRoot()
function createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, // TODO: We have several of these arguments that are conceptually part of the
// host config, but because they are passed in at runtime, we have to thread
// them through the root constructor. Perhaps we should put them all into a
// single type, like a DynamicHostConfig that is defined by the renderer.
identifierPrefix, onRecoverableError, transitionCallbacks) {var root = new FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError);// stateNode is any.var uninitializedFiber = createHostRootFiber(tag, isStrictMode);root.current = uninitializedFiber;uninitializedFiber.stateNode = root;{var _initialState = {element: initialChildren,isDehydrated: hydrate,cache: null,// not enabled yettransitions: null,pendingSuspenseBoundaries: null};uninitializedFiber.memoizedState = _initialState;}initializeUpdateQueue(uninitializedFiber);return root;
}
四、createHostRootFiber()
function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) {var mode;if (tag === ConcurrentRoot) {mode = ConcurrentMode;if (isStrictMode === true) {mode |= StrictLegacyMode;{mode |= StrictEffectsMode;}}} else {mode = NoMode;}if ( isDevToolsPresent) {// Always collect profile timings when DevTools are present.// This enables DevTools to start capturing timing at any point–// Without some nodes in the tree having empty base times.mode |= ProfileMode;}return createFiber(HostRoot, null, null, mode);
}
五、creteFiber()
var createFiber = function (tag, pendingProps, key, mode) {// $FlowFixMe: the shapes are exact here but Flow doesn't like constructorsreturn new FiberNode(tag, pendingProps, key, mode);
};