hydrate
1 )概述
hydrate在react当中不算特别重要, 但是很多时候会用到的一个API- 这个 API 它主要作用就是在进入第一次渲染的时候,如果本身 dom 树上面已经有一个dom结构存在
- 是否可以去利用这一部分已经存在的dom,然后去避免掉在第一次渲染的时候
- 要去创建很多 dom 节点的一个过程,可以大大的提升第一次渲染的性能
- 看下 react 什么时候需要去执行
hydrate什么时候又不需要去执行它 - 在 ReactDOM.js 中找到 react 里面的两个渲染 API
- 一个是
hydrate另外一个是render - 它们接收的参数都是一样的
- 它们都调用同一个函数
legacyRenderSubtreeIntoContainer - 唯一的区别是它们传入的第4个参数不一样
- 一个是
2 )源码
定位到 packages/react-dom/src/client/ReactDOM.js#L627
hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
// TODO: throw or warn if we couldn't hydrate?
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
true,
callback,
);
}
render(
element: React$Element<any>,
container: DOMContainer,
callback: ?Function,
) {
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
对于 legacyRenderSubtreeIntoContainer 第4个参数 forceHydrate 表示是否强制融合
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: DOMContainer,
forceHydrate: boolean,
callback: ?Function,
) {
// TODO: Ensure all entry points contain this check
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
);
if (__DEV__) {
topLevelUpdateWarnings(container);
}
// TODO: Without `any` type, Flow says "Property cannot be accessed on any
// member of intersection type." Whyyyyyy.
let root: Root = (container._reactRootContainer: any);
if (!root) {
// Initial mount
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
originalCallback.call(instance);
};
}
// Initial mount should not be batched.
DOMRenderer.unbatchedUpdates(() => {
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
root.render(children, callback);
}
});
} else {
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
originalCallback.call(instance);
};
}
// Update
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
root.render(children, callback);
}
}
return DOMRenderer.getPublicRootInstance(root._internalRoot);
}
- 在调用
legacyCreateRootFromDOMContainer这个方法的时候,会传入这个 forceHydrate 这个参数function legacyCreateRootFromDOMContainer( container: DOMContainer, forceHydrate: boolean, ): Root { // 在这个方法里面,首先它声明了一个参数叫 shouldHydrate,如果 forceHydrate 为 true, shouldHydrate 也一定为 true // 不是 true, 需要调用 `shouldHydrateDueToLegacyHeuristic` 来进行判断 // 这也是目前在 react dom 环境中,即便使用了 render API,但仍然有可能会去执行 hydrate 的一个过程 // 注意,只针对目前版本 const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // First clear any existing content. if (!shouldHydrate) { let warned = false; let rootSibling; while ((rootSibling = container.lastChild)) { if (__DEV__) { if ( !warned && rootSibling.nodeType === ELEMENT_NODE && (rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME) ) { warned = true; warningWithoutStack( false, 'render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.', ); } } container.removeChild(rootSibling); } } if (__DEV__) { if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) { warnedAboutHydrateAPI = true; lowPriorityWarning( false, 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + 'will stop working in React v17. Replace the ReactDOM.render() call ' + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.', ); } } // Legacy roots are not async by default. const isConcurrent = false; // 后续在创建 ReactRoot 的时候,传入了 shouldHydrate return new ReactRoot(container, isConcurrent, shouldHydrate); }- 进入
shouldHydrateDueToLegacyHeuristicfunction shouldHydrateDueToLegacyHeuristic(container) { // 这里面我们拿到了rootElement 之后,我们首先判断的条件 // 就是它是否存在, 以及它是否是一个普通的节点 // 以及它是否有 `ROOT_ATTRIBUTE_NAME` 这个attribute // ROOT_ATTRIBUTE_NAME 的 值是 data-reactroot // 这个是在调用服务端渲染的API的时候,生成的HTML上面它的container节点上面会增加这个attribute // 这也是react服务端渲染的API帮助我们去在客户端去识别是否需要融合节点的一个帮助的attribute // 如果符合这个条件,React它就会猜测, 即便你调用的不是 Hydrate API // 它仍然猜测你是需要执行 hydrate,它的 shouldHydrate 就等于 true const rootElement = getReactRootElementInContainer(container); return !!( rootElement && rootElement.nodeType === ELEMENT_NODE && rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME) ); } - 进入
getReactRootElementInContainerfunction getReactRootElementInContainer(container: any) { if (!container) { return null; } // DOCUMENT_NODE 表示 window.document, 返回 documentElement 就是 html节点 if (container.nodeType === DOCUMENT_NODE) { return container.documentElement; } else { return container.firstChild; } }- 这也是强烈不推荐在调用 render API 的时候传入 document 的原因
- 因为如果传入了document,那么它就会对整个HTML文档来进行一个调和的过程
- 如果我不需要一个融合的过程,它可能会直接把HTML里面所有的内容都删掉,那么这就很可能会导致一些问题了
- 所以在这里面,如果是你传入的是 document,它返回的是 documentElement,那就是HTML节点
- 如果它传入的不是 document, 就是这个 container.firstChild 节点
- 比如说传入的是root节点,root节点如果它没有子节点,它的firstChild 肯定就是一个 null这种情况
- 这时候肯定不需要融合的一个过程
- 进入
ReactRootfunction ReactRoot( container: Container, isConcurrent: boolean, hydrate: boolean, ) { const root = DOMRenderer.createContainer(container, isConcurrent, hydrate); this._internalRoot = root; }- 进入
createContainerexport function createContainer( containerInfo: Container, isConcurrent: boolean, hydrate: boolean, ): OpaqueRoot { return createFiberRoot(containerInfo, isConcurrent, hydrate); }- 进入
createFiberRootexport function createFiberRoot( containerInfo: any, isConcurrent: boolean, hydrate: boolean, ): FiberRoot { // Cyclic construction. This cheats the type system right now because // stateNode is any. const uninitializedFiber = createHostRootFiber(isConcurrent); let root; if (enableSchedulerTracing) { root = ({
- 进入
- 进入
- 进入

最低0.47元/天 解锁文章
803

被折叠的 条评论
为什么被折叠?



