//这个函数只处理复杂类型的元素 //它只处理类似于<App/>和<Button />这样的,而不是<div/> function mountComposite(element) { var type = element.type; var props = element.props;
var renderedElement; if (isClass(type)) { //组件类 var publicInstance = new type(props); // 设置属性 publicInstance.props = props; //如果有必要,调用生命周期 if (publicInstance.componentWillMount) { publicInstance.componentWillMount(); } renderedElement = publicInstance.render(); } else if (typeof type === 'function') { //组件函数 renderedElement = type(props); }
// This is recursive but we'll eventually reach the bottom of recursion when //这是个递归的过程,但是当元素是主机元素时(比如<div/>而不是复杂的组件)我们最终会到达递归的底部 return mount(renderedElement); }
//这个函数只处理主机类型元素 //比如,它处理<div/>,<p/>不会出来<App/> function mountHost(element) { var type = element.type; var props = element.props; var children = props.children || []; if (!Array.isArray(children)) { children = [children]; } children = children.filter(Boolean);
//这一块的代码不会再协调器中 //不同的渲染器可能会初始化不同的节点 //比如,React Native可能会创建IOS或者Android的视图 var node = document.createElement(type); Object.keys(props).forEach(propName => { if (propName !== 'children') { node.setAttribute(propName, props[propName]); } });
//镶嵌子元素 children.forEach(childElement => { // Children may be host (e.g. <div />) or composite (e.g. <Button />). //子元素们可能是主机元素(比如<div/>)或复杂性元素(<Button/>) //我们会递归的装载他们 var childNode = mount(childElement);
function mount(element) { var type = element.type; if (typeof type === 'function') { // User-defined components return mountComposite(element); } else if (typeof type === 'string') { // Platform-specific components return mountHost(element); } }
var rootEl = document.getElementById('root'); var node = mount(<App />); rootEl.appendChild(node);
function instantiateComponent(element) { var type = element.type; if(typeof type === 'function') { //用户定义的组件 return new CompositeComponent(element); }else if(typeof === 'string') { //平台组件 return new DOMComponent(element); } }
function unmountTree(containerNode) { //从DOM节点中读取内部实例 //(这个还不能工作,我们需要改变mountTree来来储存它) var node = containerNode.firstChild; var rootComponent = node._internalInstance;
receive(nextElement) { var prevProps = this.currentElement.props; var publicInstance = this.publicInstance; var prevRenderedComponent = this.renderedComponent; var prevRenderedElement = this.renderedElement;
//修改*自己的*元素 this.currentElement = nextElement; var type = nextElement.type; var nextProps = nextElement.props;
//找到老节点因为它将被替换 var prevNode = prevRenderedComponent.getHostNode();
//卸载老child并装载新的child prevRenderedComponent.unmount(); var nextRenderedComponent = instantiateComponent(nextRenderedElement); var nextNode = nextRenderedComponent.mount();
getHostNode() { // Ask the rendered component to provide it. // This will recursively drill down any composites. return this.renderedComponent.getHostNode(); } }
class DOMComponent { //... receive(nextElement) { var node = this.node; var prevElement = this.currentElement; var prevProps = prevElement.props; var nextProps = nextElement.props; this.currentElement = nextElement;
// Remove old attributes. Object.keys(prevProps).forEach(propName => { if (propName !== 'children' && !nextProps.hasOwnProperty(propName)) { node.removeAttribute(propName); } }); // Set next attributes. Object.keys(nextProps).forEach(propName => { if (propName !== 'children') { node.setAttribute(propName, nextProps[propName]); } });
//这是React元素的数组 var prevChildren = prevProps.children || []; if (!Array.isArray(prevChildren)) { prevChildren = [prevChildren]; } var nextChildren = nextProps.children || []; if (!Array.isArray(nextChildren)) { nextChildren = [nextChildren]; } //这是内部实例的数组 var prevRenderedChildren = this.renderedChildren; var nextRenderedChildren = [];
//当我们遍历children时,我们要添加对这个数组的一些操作 var operationQueue = [];
//注意:下面的部分非常简单,他不会处理重新排序,它的存在只是为了说明整体流程,但不具体 for (var i = 0; i < nextChildren.length; i++) { // Try to get an existing internal instance for this child var prevChild = prevRenderedChildren[i];
// If there is no internal instance under this index, // a child has been appended to the end. Create a new // internal instance, mount it, and use its node. if (!prevChild) { var nextChild = instantiateComponent(nextChildren[i]); var node = nextChild.mount();
// Record that we need to append a node operationQueue.push({type: 'ADD', node}); nextRenderedChildren.push(nextChild); continue; }
// We can only update the instance if its element's type matches. // For example, <Button size="small" /> can be updated to // <Button size="large" /> but not to an <App />. var canUpdate = prevChildren[i].type === nextChildren[i].type;
// If we can't update an existing instance, we have to unmount it // and mount a new one instead of it. if (!canUpdate) { var prevNode = prevChild.node; prevChild.unmount();
var nextChild = instantiateComponent(nextChildren[i]); var nextNode = nextChild.mount();
// Record that we need to swap the nodes operationQueue.push({type: 'REPLACE', prevNode, nextNode}); nextRenderedChildren.push(nextChild); continue; }
// If we can update an existing internal instance, // just let it receive the next element and handle its own update. prevChild.receive(nextChildren[i]); nextRenderedChildren.push(prevChild); }
// Finally, unmount any children that don't exist: for (var j = nextChildren.length; j < prevChildren.length; j++) { var prevChild = prevRenderedChildren[j]; var node = prevChild.node; prevChild.unmount();
// Record that we need to remove the node operationQueue.push({type: 'REMOVE', node}); }
// Point the list of rendered children to the updated version. this.renderedChildren = nextRenderedChildren;
// ...
最后一步,我们执行DOM操作,真实的reconciler是很复杂的因为它还得处理移动:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Process the operation queue. while (operationQueue.length > 0) { var operation = operationQueue.shift(); switch (operation.type) { case 'ADD': this.node.appendChild(operation.node); break; case 'REPLACE': this.node.replaceChild(operation.nextNode, operation.prevNode); break; case 'REMOVE': this.node.removeChild(operation.node); break; } } } }
function mountTree(element, containerNode) { // Check for an existing tree if (containerNode.firstChild) { var prevNode = containerNode.firstChild; var prevRootComponent = prevNode._internalInstance; var prevElement = prevRootComponent.currentElement;
// If we can, reuse the existing root component if (prevElement.type === element.type) { prevRootComponent.receive(element); return; }
// Otherwise, unmount the existing tree unmountTree(containerNode); }