React源码学习

2018-04-12

##Virtual DOM模型

  • Virtual DOM模型组成:

    • 标签名
    • 节点属性: 样式, 属性和事件等
    • 子节点
    • 标识ID
      1
      2
      3
      4
      5
      6
      7
      {
      tagName: "div",
      properties: {
      style: {}
      },
      children: {},
      key: 1,
  • Virtual DOM节点分类

    • ReactElement
      • ReactComponentElement
      • ReactDOMElement
    • ReactFragment
    • ReactText

创建React 元素

  • 通过JSX创建的虚拟元素最终会被Babel编译成调用React的createElement方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const Nav, Profile;
    // 原始JSX
    const app = <Nav color="blue"><Profile>click</Profile></Nav>;
    // 转译后
    const app = React.createElement(
    Nav,
    {color:"blue"},
    React.createElement(Profile, null, "click")
    )
  • createElement工作原理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    ReactElement.createElement = function(type, config, children) {
    // 初始化参数
    var propName;
    var props = {};
    var key = null;
    var ref = null;
    var self = null;
    var source = null;
    // 如果存在config, 则提取里面的内容
    if ( config != null ){
    ref = config.ref === undefined ? null : config.ref;
    key = config.key === undefined ? null : config.key;
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // 复制其他config的内容到props
    for (propName in config) {
    if (config.hasOwnProperty(propName) &&
    !RESERVED_PROPS.hasOwnProperty(propName)){
    props[propName] = config[propName];
    }
    }
    }
    // 处理children, 全部挂载到props的children属性上,如果只有一个参数, 直接赋值给children,否则合并处理
    var childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
    props.children = children;
    } else if (childrenLength > 1) {
    var childArray = Array(childrenLength);
    for (var i = 0; i < childrenLength; i++){
    childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
    }
    // 如果某个 prop 为空且存在默认的 prop, 则将默认的 prop 赋值给当前prop
    if ( type && type.defaultProps ){
    var defaultProps = type.defaultProps;
    for (propName in defaultProps) {
    if ( typeof props[propName] === "undefined" ){
    props[propName] = defaultProps[propName];
    }
    }
    }
    }

创建组件入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function instantiateReactComponent(node, parentCompositeType) {
var instance;

// 如果是空组件 (ReactEmptyComponent)
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
}

if (typeof node === 'object') {
var element = node;
if( typeof element.type === 'string') {
// DOM标签 (ReactDOMComponent)
instance = ReactNativeComponent.createInternalComponent(element);
} else if ( isInternalComponentType(element.type)){
// 不是字符串表示的自定义组件暂时无法使用, 此处将不做组件初始化操作
instance = new element.type(element);
} else {
// 自定义组件 (ReactCompositeComponent)
instance = new ReactCompositeComponentWrapper();
}
} else if (typeof node === 'string' || typeof node === 'number') {
// 字符串或者数字
instance = ReactNativeComponent.createInstanceForText(node);
} else {
// do nothing
}
// 设置实例
instance.construct(node);
// 初始化参数
instance._mountIndex = 0;
instance._mountImage = null;

return instance;
}

创建文本组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
var ReactDOMTextComponent = function(text){
// 保存当前的字符串
this._currentElement = text;
this._stringText = '' + text;

// ReactDOMComponentTree 需要使用的参数
this._nativeNode = null;
this._nativeParent = null;

// 属性
this._domID = null;
this._mountIndex = 0;
this._closingComment = null;
this._commentNodes = null;
};
Object.assign(ReactDOMTextComponent.prototype, {
mountComponent: function(transaction, nativeParent, nativeContainerInfo, context) {
var domID = nativeContainerInfo.__idCounter++;
var openingValue = ' react-text' + domID + ' ';
car closingValue = ' /react-text ';
this._domID = domID;
this._nativeParent = nativeParent;

// 如果使用 createElement 创建文件标签, 则该文本会带上标签和 domID
if (transaction.useCreateElement) {
var ownerDocument = nativeContainerInfo._ownerDocument;
var openingComment = ownerDocument.createComment(openingValue);
var closingComment = ownerDocument.createComment(closingValue);
// 开始标签
DOMLazyTree.queueChild(laryTree, DOMLazyTree(openingComment));
// 如果是文本类型, 则创建文本节点
if (this._stringText) {
DOMLazyTree.queueChild(laryTree, DOMLazyTree(ownerDocument.createTextNODE(this._stringText)));
}
// 结束标签
DOMLazyTree.queueChild(lazyTree, DOMLazyTree(closingComment));
ReactDomComponentTree.precacheNode(this, openingComment);
this._closingComment = closingComment;
return lazyTree;
} else {
var escapedText = escapeTextContentForBrowser(this._stringText);
// 静态页面下直接返回文本
if (transaction.renderToStaticMarkup) {
return escapedText;
}
// 如果不是通过 createElement 创建的文本, 则将标签和属性注释掉, 直接返回文本内容
return (
'<!--' + openingValue + '-->' + escapedText +
'<!--' + closingValue + '-->'
);
}
},
// 更新文本内容
receiveComponent: function(nextText, transaction) {
if (nextText !== this._currentElement) {
this.__currentElement = nextText;
var nextStringText = '' + nextText;
if (nextStringText !== this._stringText) {
this._stringText = nextStringText;
var commentNodes = this.getNativeNode();

DOMChildrenOperactions.replaceDelimitedText(commentNodes[0], commentNodes[1], nextStringTex);
}
}
}
})

DOM 标签组件

`javascript
_createOpenTagMarkupAndPutListeners: function(transaction, props) {
var ret = ‘<’ + this._currentElement.type;
// 拼凑出属性
for ( var propKey in props) {
var propValue = props[propKey];

if (registrationNameModules.hasOwnProperty(propKey)) {
  // 针对当前的节点添加事件代理
  if (propValue) {
    enqueuePutListener(this, propKey, propValue, transaction);
  }
} else {
  if (propKey === STYLE) {
    if (propValue) {
      // 合并样式
      propValue = this._previousStyleCopy = Object.assign({}, props.style);
    }
    propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this);
  }
  // 创建属性标识
  var markup = null;
  if (this._tag != null && isCustomComponent(this._tag, props)){
    markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
  }
  if (markup) {
    ret += ' ' + markup;
  }
}

}

// 对于静态页面, 不需要设置 react-id, 节省大量字节
if (transaction.renderToStaticMarkup) {
return ret;
}

// 设置 react-id
if (!this._nativeParent) {
ret += ‘’ + DOMPropertyOperations.createMarkupForRoot();
}
ret += ‘’ + DOMPropertyOperations.createMarkupForID(this._domID);

return ret;
}