reconciliation 的设计
DOM仅是React支持的一个渲染环境,通过React Native它还可以支持原生IOS 和 Android页面的渲染。React 之所以能够支持如此多的渲染环境,主要是因为在设计上 reconciliation 和 rendering 是分离的阶段。reconciliation 负责计算树的哪些部分发生了变化;rendering 使用该信息来实际更新app。
reconciliation是一个过程,这个过程类似于Virtual Dom中所描述的。不同场景配置的api相似,但执行代码不同。react-reconciler支持React ART、React DOM、React Native。配置地址为:
virtual Dom
当渲染一个React 应用,会生成一棵描述应用结构的节点树,并保存在内存中。然后将此树刷新到呈现环境——例如,在浏览器应用程序的情况下,它被转换成一组DOM操作。也就是16前说的“Virtual Dom”。
虚拟DOM(VDOM)是一种编程概念(或称为“virtual”),是指虚拟的视图被保存在内存中,并通过诸如ReactDOM的库与“真实”DOM同步。这个过程被称为reconciliation。
在16版后,fibers 实现‘virtual Dom’的功能。‘virtual Dom’ 在不同的场景,指代的意思可能不一样(可Google)。16版后用fiber实现‘virtual’,使用 reconciliation 来代替这个过程。
什么是‘React Fiber’
在React 16中,Fiber是 reconciliation 引擎。fiber的主要目的是使 virtual DOM 能够进行增量渲染。
reconciliation api(接口)
支持的api为:
// packages\react-reconciler\src\forks\ReactFiberHostConfig.custom.js
export const getPublicInstance = $$$hostConfig.getPublicInstance;
export const getRootHostContext = $$$hostConfig.getRootHostContext;
export const getChildHostContext = $$$hostConfig.getChildHostContext;
export const prepareForCommit = $$$hostConfig.prepareForCommit;
export const resetAfterCommit = $$$hostConfig.resetAfterCommit;
export const createInstance = $$$hostConfig.createInstance;
export const appendInitialChild = $$$hostConfig.appendInitialChild;
export const finalizeInitialChildren = $$$hostConfig.finalizeInitialChildren;
export const prepareUpdate = $$$hostConfig.prepareUpdate;
export const shouldSetTextContent = $$$hostConfig.shouldSetTextContent;
export const shouldDeprioritizeSubtree =
$$$hostConfig.shouldDeprioritizeSubtree;
export const createTextInstance = $$$hostConfig.createTextInstance;
export const scheduleDeferredCallback = $$$hostConfig.scheduleDeferredCallback;
export const cancelDeferredCallback = $$$hostConfig.cancelDeferredCallback;
export const shouldYield = $$$hostConfig.shouldYield;
export const scheduleTimeout = $$$hostConfig.setTimeout;
export const cancelTimeout = $$$hostConfig.clearTimeout;
export const noTimeout = $$$hostConfig.noTimeout;
export const now = $$$hostConfig.now;
export const isPrimaryRenderer = $$$hostConfig.isPrimaryRenderer;
export const supportsMutation = $$$hostConfig.supportsMutation;
export const supportsPersistence = $$$hostConfig.supportsPersistence;
export const supportsHydration = $$$hostConfig.supportsHydration;
// -------------------
// Mutation
// (optional)
// -------------------
export const appendChild = $$$hostConfig.appendChild;
export const appendChildToContainer = $$$hostConfig.appendChildToContainer;
export const commitTextUpdate = $$$hostConfig.commitTextUpdate;
export const commitMount = $$$hostConfig.commitMount;
export const commitUpdate = $$$hostConfig.commitUpdate;
export const insertBefore = $$$hostConfig.insertBefore;
export const insertInContainerBefore = $$$hostConfig.insertInContainerBefore;
export const removeChild = $$$hostConfig.removeChild;
export const removeChildFromContainer = $$$hostConfig.removeChildFromContainer;
export const resetTextContent = $$$hostConfig.resetTextContent;
export const hideInstance = $$$hostConfig.hideInstance;
export const hideTextInstance = $$$hostConfig.hideTextInstance;
export const unhideInstance = $$$hostConfig.unhideInstance;
export const unhideTextInstance = $$$hostConfig.unhideTextInstance;
// -------------------
// Persistence
// (optional)
// -------------------
export const cloneInstance = $$$hostConfig.cloneInstance;
export const createContainerChildSet = $$$hostConfig.createContainerChildSet;
export const appendChildToContainerChildSet = $$$hostConfig.appendChildToContainerChildSet;
export const finalizeContainerChildren = $$$hostConfig.finalizeContainerChildren;
export const replaceContainerChildren = $$$hostConfig.replaceContainerChildren;
export const cloneHiddenInstance = $$$hostConfig.cloneHiddenInstance;
export const cloneUnhiddenInstance = $$$hostConfig.cloneUnhiddenInstance;
export const createHiddenTextInstance = $$$hostConfig.createHiddenTextInstance;
// -------------------
// Hydration
// (optional)
// -------------------
export const canHydrateInstance = $$$hostConfig.canHydrateInstance;
export const canHydrateTextInstance = $$$hostConfig.canHydrateTextInstance;
export const getNextHydratableSibling = $$$hostConfig.getNextHydratableSibling;
export const getFirstHydratableChild = $$$hostConfig.getFirstHydratableChild;
export const hydrateInstance = $$$hostConfig.hydrateInstance;
export const hydrateTextInstance = $$$hostConfig.hydrateTextInstance;
export const didNotMatchHydratedContainerTextInstance =
$$$hostConfig.didNotMatchHydratedContainerTextInstance;
export const didNotMatchHydratedTextInstance =
$$$hostConfig.didNotMatchHydratedTextInstance;
export const didNotHydrateContainerInstance =
$$$hostConfig.didNotHydrateContainerInstance;
export const didNotHydrateInstance = $$$hostConfig.didNotHydrateInstance;
export const didNotFindHydratableContainerInstance =
$$$hostConfig.didNotFindHydratableContainerInstance;
export const didNotFindHydratableContainerTextInstance =
$$$hostConfig.didNotFindHydratableContainerTextInstance;
export const didNotFindHydratableInstance = $$$hostConfig.didNotFindHydratableInstance;
export const didNotFindHydratableTextInstance =$$$hostConfig.didNotFindHydratableTextInstance;
这些api并不都是必传,其中有Mutation、Persistence、Hydration中的api为可选的。
reconciliation 编译包
下载react源码,并执行
yarn run build
将生成各种不同的包,多的出乎你的想象,此处留作彩蛋,自己动手看。
react-reconciler 希望使用方提供配置,在编译react的时候,编译工具需要提供的场景进行相关的配置。在react 源码中,配置代码如下:
//... react\scripts\rollup\forks.js 289行代码
for (let rendererInfo of inlinedHostConfigs) {
if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
return `react-reconciler/src/forks/ReactFiberHostConfig.${
rendererInfo.shortName
}.js`;
}
}
//..
根据 react-reconciler/src/forks 文件夹下的文件后缀,引入不同的配置。这些shortName就是react-concilier支持的场景。inlinedHostConfigs 指向一个配置文件,配置代码如下:
module.exports = [
{
shortName: 'dom',
entryPoints: ['react-dom', 'react-dom/unstable-fizz.node'],
isFlowTyped: true,
isFizzSupported: true,
},
{
shortName: 'dom-browser',
entryPoints: ['react-dom/unstable-fizz.browser'],
isFlowTyped: true,
isFizzSupported: true,
},
{
shortName: 'fire',
entryPoints: ['react-dom/unstable-fire'],
isFlowTyped: true,
},
{
shortName: 'art',
entryPoints: ['react-art'],
isFlowTyped: false, // TODO: type it.
isFizzSupported: false,
},
{
shortName: 'native',
entryPoints: ['react-native-renderer'],
isFlowTyped: true,
isFizzSupported: false,
},
{
shortName: 'fabric',
entryPoints: ['react-native-renderer/fabric'],
isFlowTyped: true,
isFizzSupported: false,
},
{
shortName: 'test',
entryPoints: ['react-test-renderer'],
isFlowTyped: true,
isFizzSupported: false,
},
{
shortName: 'custom',
entryPoints: [
'react-reconciler',
'react-reconciler/persistent',
'react-stream',
],
isFlowTyped: true,
isFizzSupported: true,
},
];
由上面的代码可以看出,16版后react-conciler 支持的场景有:dom、dom-browser、fire、art、native、fabric、test、custom。