|
文章背景 本文主要讲两部分内容,一是介绍大致的render函数执行的主流程,包括挂载和渲染jsx节点,对内部调用的函数进行讲解;另一个是细化render阶段做的一些重要内容,包括Fiber的内部计算和任务调度的相关内容。进入正题 我们知道,jsx元素要在页面上渲染出来,首先需要调用React.createElement->React.render(FiberRootNode)->创建更新对象->处理更新队列(任务调度)->进入commit阶段->渲染完成 这是render函数执行的一个主流程 那么,我们先来看看React.createElement函数。React.createElement//JSX代码1// 转换成React.createElement("div", { id: "1"}, "1")createElement函数将会接收三个参数:type一般为DOM节点名称或类组件、函数组件,config中包含着ref,key,props等配置参数,children就是这个DOM节点内部包含的内容,它可能是前面举例的普通元素,也可能是一个数组。在当前函数中都会做一个判断处理。function createElement(type, config, children) { var propName; var props = {}; var key = null; var ref = null; var self = null; var source = null; if (config != null) { if (hasValidRef(config)) { ref = config.ref; { warnIfStringRefCannotBeAutoConverted(config); } } if (hasValidKey(config)) { key = '' + config.key; } self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object for (propName in config) { if (hasOwnProperty.call(config, propName) & !RESERVED_PROPS.hasOwnProperty(propName)) { props[propName] = config[propName]; } } } var childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { var childArray = Array(childrenLength); for (var i = 0; i , document.getElementById('root') 这行代码告诉React应用,我们想在某个容器中渲染出一个组件。那么我们现在就来看看render这个函数到底有什么内容。 可以看到,它的第一个参数就是前面生成的ReactElement对象。function render(element, container, callback) { return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);} render会调用legacyRenderSubtreeIntoContainer进行挂载和更新function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) { var root = container._reactRootContainer; var fiberRoot; if (!root) { // 通过调用legacyCreateRootFromDOMContainer方法将其返回值赋值给container._reactRootContainer root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate); fiberRoot = root._internalRoot; if (typeof callback === 'function') { var originalCallback = callback; callback = function () { var instance = getPublicRootInstance(fiberRoot); originalCallback.call(instance); }; } unbatchedUpdates(function () { updateContainer(children, fiberRoot, parentComponent, callback); }); } else { fiberRoot = root._internalRoot; if (typeof callback === 'function') { var _originalCallback = callback; callback = function () { var instance = getPublicRootInstance(fiberRoot); _originalCallback.call(instance); }; } updateContainer(children, fiberRoot, parentComponent, callback); } return getPublicRootInstance(fiberRoot);} 当我们第一次启动运行项目的时候,也就是第一次执行ReactDOM.render方法的时候,这时去获取container._reactRootContainer肯定是没有值的,所以我们暂时只关心没有root时的代码处理: 当没有root时,调用legacyCreateRootFromDOMContainer方法将其返回值赋值给container._reactRootContainer,并创建FiberRoot;因为初始化不能批量处理,即同步更新,直接调用unbatchedUpdates方法,这个方法的内容后面再说。function legacyCreateRootFromDOMContainer(container, forceHydrate) { // 判断是否需要融合 var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // 针对客户端渲染的情况,需要将container容器中的所有元素移除 if (!shouldHydrate) { var warned = false; var rootSibling; while (rootSibling = container.lastChild) { container.removeChild(rootSibling); } } // 返回一个LegacyRoot实例 return createLegacyRoot(container, shouldHydrate ? { hydrate: true } : undefined);} 这里会调用shouldHydrateDueToLegacyHeuristic函数去判断是否需要融合,当客户端渲染的情况下,需要将容器里的所有元素移除。结尾处调用createLegacyRoot函数去new一个ReactDOMLegacyRoot实例。function createLegacyRoot(container, options) { return new ReactDOMLegacyRoot(container, options);} newReactDOMLegacyRoot的内部会执行createRootImpl函数function ReactDOMLegacyRoot(container, options) { this._internalRoot = createRootImpl(container, LegacyRoot, options);} 这部分我们要去区分一下FiberRoot和RootFiber的区别和他们之间的联系,往下看function createRootImpl(container, tag, options) { // Tag is either LegacyRoot or Concurrent Root var hydrate = options != null & options.hydrate === true; var hydrationCallbacks = options != null & options.hydrationOptions || null; var mutableSources = options != null & options.hydrationOptions != null & options.hydrationOptions.mutableSources || null; var isStrictMode = options != null & options.unstable_strictMode === true; // 创建一个fiberRoot var root = createContainer(container, tag, hydrate, hydrationCallbacks, isStrictMode); // 给container附加一个内部属性用于指向fiberRoot的current属性对应的rootFiber节点 markContainerAsRoot(root.current, container); var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container; listenToAllSupportedEvents(rootContainerElement); if (mutableSources) { for (var i = 0; i
|
|