理解ReactElement和ReactClass的概念

首先让我们理解两个概念:
ReactElement
一个描述DOM节点或component实例的字面级对象。它包含一些信息,包括组件类型type和属性props。就像一个描述DOM节点的元素(虚拟节点)。它们可以被创建通过React.createElement方法或jsx写法
分为DOM Element和Component Elements两类:
当节点的type属性为字符串时,它代表是普通的节点,如div,span
- {
 - 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!'
 - }
 - }
 
ReactClass
ReactClass是平时我们写的Component组件(类或函数),例如上面的Button类。ReactClass实例化后调用render方法可返回DOM Element。
react渲染过程
过程理解:
- // element是 Component Elements
 - ReactDOM.render({
 - type: Form,
 - props: {
 - isSubmitted: false,
 - buttonText: 'OK!'
 - }
 - }, document.getElementById('root'));
 
react更新机制
每个类型的元素都要处理好自己的更新:
***步:调用this.setState
- ReactClass.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,
 - nextState,
 - nextContext,
 - transaction,
 - nextUnmaskedContext
 - );
 - } else {
 - // 不更新元素,但仍然设置props和state
 - this._currentElement = nextParentElement;
 - this._context = nextUnmaskedContext;
 - inst.props = nextProps;
 - inst.state = nextState;
 - inst.context = nextContext;
 - }
 - // .......
 - }
 
内部_performComponentUpdate方法源码
- 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, transaction, context) {
 - var prevElement = this._currentElement;
 - this._currentElement = nextElement;
 - this.updateComponent(transaction, prevElement, nextElement, context);
 - }
 
updateComponent方法源码
- ReactDOMComponent.prototype.updateComponent = function(transaction, prevElement, nextElement, context) {
 - // 略.....
 - //需要单独的更新属性
 - this._updateDOMProperties(lastProps, nextProps, transaction, isCustomComponentTag);
 - //再更新子节点
 - this._updateDOMChildren(
 - lastProps,
 - nextProps,
 - transaction,
 - context
 - );
 - // ......
 - }
 
this._updateDOMChildren方法内部调用diff算法,请看下一节........
react Diff算法
diff算法源码
- _updateChildren: function(nextNestedChildrenElements, transaction, context) {
 - var prevChildren = this._renderedChildren;
 - var removedNodes = {};
 - var mountImages = [];
 - // 获取新的子元素数组
 - var nextChildren = this._reconcilerUpdateChildren(
 - prevChildren,
 - nextNestedChildrenElements,
 - mountImages,
 - removedNodes,
 - transaction,
 - 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],
 - lastPlacedNode,
 - nextIndex,
 - transaction,
 - 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的ReactElement节点分解成多个组件。在需要优化的组件手动添加 shouldComponentUpdate 来避免不需要的 re-render。
                本文标题:深入理解react(源码分析)
                
                标题链接:http://www.csdahua.cn/qtweb/news10/429610.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网