深入理解react(源码分析)
原文链接
理解ReactElement和ReactClass的概念首先让我们理解两个概念: ReactElement一个描述DOM节点或component实例的字面级对象。它包含一些信息,包括组件类型 分为
当节点的type属性为字符串时,它代表是普通的节点,如 { type: 'button',props: { className: 'button button-blue',children: { type: 'b',props: { children: 'OK!' } } } }
当节点的type属性为一个函数或一个类时,它代表自定义的节点 class Button extends React.Component { render() { const { children,color } = this.props; return { type: 'button',props: { className: 'button button-' + color,children: { type: 'b',props: { children: children } } } }; } } // Component Elements { type: Button,props: { color: 'blue',children: 'OK!' } } ReactClassReactClass是平时我们写的Component组件(类或函数),例如上面的 react渲染过程
过程理解: // element是 Component Elements ReactDOM.render({ type: Form,props: { isSubmitted: false,buttonText: 'OK!' } },document.getElementById('root'));
react更新机制
每个类型的元素都要处理好自己的更新:
第一步:调用this.setStateReactClass.prototype.setState = function(newState) { //this._reactInternalInstance是ReactCompositeComponent的实例 this._reactInternalInstance.receiveComponent(null,newState); } 第二步:调用内部receiveComponent方法这里主要分三种情况,文本元素,基本元素,自定义元素。 自定义元素:receiveComponent方法源码 // receiveComponent方法 ReactCompositeComponent.prototype.receiveComponent = function(nextElement,transaction,nextContext) { var prevElement = this._currentElement; var prevContext = this._context; this._pendingElement = null; this.updateComponent( transaction,prevElement,nextElement,prevContext,nextContext ); } updateComponent方法源码 // updateComponent方法 ReactCompositeComponent.prototype.updateComponent = function( transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext ) { // 简写..... // 不是state更新而是props更新 if (prevParentElement !== nextParentElement) { willReceive = true; } if (willReceive && inst.componentWillReceiveProps) { // 调用生命周期componentWillReceiveProps方法 } // 是否更新元素 if (inst.shouldComponentUpdate) { // 如果提供shouldComponentUpdate方法 shouldUpdate = inst.shouldComponentUpdate(nextProps,nextState,nextContext); } else { if (this._compositeType === CompositeTypes.PureClass) { // 如果是PureClass,浅层对比props和state shouldUpdate = !shallowEqual(prevProps,nextProps) || !shallowEqual(inst.state,nextState); } } if (shouldUpdate) { // 更新元素 this._performComponentUpdate( nextParentElement,nextProps,nextContext,nextUnmaskedContext ); } else { // 不更新元素,但仍然设置props和state this._currentElement = nextParentElement; this._context = nextUnmaskedContext; inst.props = nextProps; inst.state = nextState; inst.context = nextContext; } // ....... } 内部_performComponentUpdate方法源码 // 内部_updateRenderedComponentWithNextElement方法 ReactCompositeComponent.prototype._updateRenderedComponentWithNextElement = function() { // 判定两个element需不需要更新 if (shouldUpdateReactComponent(prevRenderedElement,nextRenderedElement)) { // 如果需要更新,就继续调用子节点的receiveComponent的方法,传入新的element更新子节点。 ReactReconciler.receiveComponent( prevComponentInstance,nextRenderedElement,this._processChildContext(context) ); } else { // 卸载之前的子节点,安装新的子节点 var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance); ReactReconciler.unmountComponent( prevComponentInstance,safely,false /* skipLifecycle */ ); var nodeType = ReactNodeTypes.getType(nextRenderedElement); this._renderedNodeType = nodeType; var child = this._instantiateReactComponent( nextRenderedElement,nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */ ); this._renderedComponent = child; var nextMarkup = ReactReconciler.mountComponent( child,this._hostParent,this._hostContainerInfo,this._processChildContext(context),debugID ); } shouldUpdateReactComponent函数源码 function shouldUpdateReactComponent(prevElement,nextElement){ var prevEmpty = prevElement === null || prevElement === false; var nextEmpty = nextElement === null || nextElement === false; if (prevEmpty || nextEmpty) { return prevEmpty === nextEmpty; } var prevType = typeof prevElement; var nextType = typeof nextElement; if (prevType === 'string' || prevType === 'number') { // 如果先前的ReactElement对象类型是字符串或数字,新的ReactElement对象类型也是字符串或数字,则需要更新,新的ReactElement对象类型是对象,则不应该更新,直接替换。 return (nextType === 'string' || nextType === 'number'); } else { // 如果先前的ReactElement对象类型是对象,新的ReactElement对象类型也是对象,并且标签类型和key值相同,则需要更新 return ( nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key ); } } 文本元素:receiveComponent方法源码 ReactDOMTextComponent.prototype.receiveComponent(nextText,transaction) { //跟以前保存的字符串比较 if (nextText !== this._currentElement) { this._currentElement = nextText; var nextStringText = '' + nextText; if (nextStringText !== this._stringText) { this._stringText = nextStringText; var commentNodes = this.getHostNode(); // 替换文本元素 DOMChildrenOperations.replaceDelimitedText( commentNodes[0],commentNodes[1],nextStringText ); } } } 基本元素:receiveComponent方法源码 ReactDOMComponent.prototype.receiveComponent = function(nextElement,context) { var prevElement = this._currentElement; this._currentElement = nextElement; this.updateComponent(transaction,context); } updateComponent方法源码 ReactDOMComponent.prototype.updateComponent = function(transaction,context) { // 略..... //需要单独的更新属性 this._updateDOMProperties(lastProps,isCustomComponentTag); //再更新子节点 this._updateDOMChildren( lastProps,context ); // ...... }
react Diff算法
diff算法源码 _updateChildren: function(nextNestedChildrenElements,context) { var prevChildren = this._renderedChildren; var removedNodes = {}; var mountImages = []; // 获取新的子元素数组 var nextChildren = this._reconcilerUpdateChildren( prevChildren,nextNestedChildrenElements,mountImages,removedNodes,context ); if (!nextChildren && !prevChildren) { return; } var updates = null; var name; var nextIndex = 0; var lastIndex = 0; var nextMountIndex = 0; var lastPlacedNode = null; for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } var prevChild = prevChildren && prevChildren[name]; var nextChild = nextChildren[name]; if (prevChild === nextChild) { // 同一个引用,说明是使用的同一个component,所以我们需要做移动的操作 // 移动已有的子节点 // NOTICE:这里根据nextIndex,lastIndex决定是否移动 updates = enqueue( updates,this.moveChild(prevChild,lastPlacedNode,nextIndex,lastIndex) ); // 更新lastIndex lastIndex = Math.max(prevChild._mountIndex,lastIndex); // 更新component的.mountIndex属性 prevChild._mountIndex = nextIndex; } else { if (prevChild) { // 更新lastIndex lastIndex = Math.max(prevChild._mountIndex,lastIndex); } // 添加新的子节点在指定的位置上 updates = enqueue( updates,this._mountChildAtIndex( nextChild,mountImages[nextMountIndex],context ) ); nextMountIndex++; } // 更新nextIndex nextIndex++; lastPlacedNode = ReactReconciler.getHostNode(nextChild); } // 移除掉不存在的旧子节点,和旧子节点和新子节点不同的旧子节点 for (name in removedNodes) { if (removedNodes.hasOwnProperty(name)) { updates = enqueue( updates,this._unmountChild(prevChildren[name],removedNodes[name]) ); } } } react的优点与总结优点
总结想要更好的利用react的虚拟DOM,diff算法的优势,我们需要正确的优化、组织react页面。例如将一个页面render的 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |