react 0.8.0 → 0.9.0-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -8
- package/addons.js +0 -3
- package/lib/AutoFocusMixin.js +30 -0
- package/lib/CSSCore.js +22 -21
- package/lib/CSSProperty.js +31 -0
- package/lib/ChangeEventPlugin.js +26 -4
- package/lib/ClientReactRootIndex.js +30 -0
- package/lib/CompositionEventPlugin.js +57 -9
- package/lib/DOMChildrenOperations.js +32 -2
- package/lib/DOMProperty.js +2 -0
- package/lib/DOMPropertyOperations.js +14 -1
- package/lib/Danger.js +8 -7
- package/lib/DefaultDOMPropertyConfig.js +12 -2
- package/lib/EnterLeaveEventPlugin.js +37 -4
- package/lib/EventConstants.js +3 -0
- package/lib/EventListener.js +42 -34
- package/lib/EventPluginHub.js +113 -12
- package/lib/EventPluginRegistry.js +39 -16
- package/lib/EventPluginUtils.js +32 -3
- package/lib/EventPropagators.js +6 -42
- package/lib/ExecutionEnvironment.js +3 -0
- package/lib/LinkedValueUtils.js +161 -0
- package/lib/PooledClass.js +6 -0
- package/lib/React.js +27 -3
- package/lib/ReactCSSTransitionGroup.js +65 -0
- package/lib/{ReactTransitionableChild.js → ReactCSSTransitionGroupChild.js} +21 -35
- package/lib/ReactComponent.js +87 -52
- package/lib/ReactComponentBrowserEnvironment.js +67 -49
- package/lib/ReactComponentEnvironment.js +2 -0
- package/lib/ReactCompositeComponent.js +547 -112
- package/lib/ReactContext.js +67 -0
- package/lib/ReactDOM.js +13 -0
- package/lib/ReactDOMButton.js +4 -0
- package/lib/ReactDOMComponent.js +46 -21
- package/lib/ReactDOMForm.js +9 -2
- package/lib/ReactDOMIDOperations.js +105 -60
- package/lib/ReactDOMImg.js +58 -0
- package/lib/ReactDOMInput.js +26 -14
- package/lib/ReactDOMOption.js +1 -0
- package/lib/ReactDOMSelect.js +36 -17
- package/lib/ReactDOMTextarea.js +12 -8
- package/lib/ReactDefaultInjection.js +50 -26
- package/lib/ReactDefaultPerf.js +207 -370
- package/lib/ReactDefaultPerfAnalysis.js +199 -0
- package/lib/ReactErrorUtils.js +5 -14
- package/lib/ReactEventEmitter.js +141 -145
- package/lib/ReactEventEmitterMixin.js +0 -32
- package/lib/ReactEventTopLevelCallback.js +32 -12
- package/lib/ReactInjection.js +39 -0
- package/lib/ReactInstanceHandles.js +35 -19
- package/lib/ReactLink.js +1 -1
- package/lib/ReactMount.js +127 -103
- package/lib/ReactMountReady.js +1 -1
- package/lib/ReactMultiChild.js +30 -46
- package/lib/ReactMultiChildUpdateTypes.js +2 -0
- package/lib/ReactOwner.js +10 -2
- package/lib/ReactPerf.js +5 -8
- package/lib/ReactPropTransferer.js +40 -21
- package/lib/ReactPropTypeLocationNames.js +31 -0
- package/lib/ReactPropTypeLocations.js +29 -0
- package/lib/ReactPropTypes.js +248 -47
- package/lib/ReactPutListenerQueue.js +61 -0
- package/lib/ReactReconcileTransaction.js +20 -0
- package/lib/ReactRootIndex.js +36 -0
- package/lib/ReactServerRendering.js +8 -11
- package/lib/ReactTextComponent.js +8 -3
- package/lib/{ReactTransitionKeySet.js → ReactTransitionChildMapping.js} +42 -47
- package/lib/ReactTransitionGroup.js +132 -57
- package/lib/ReactUpdates.js +14 -11
- package/lib/ReactWithAddons.js +7 -2
- package/lib/SelectEventPlugin.js +22 -39
- package/lib/ServerReactRootIndex.js +36 -0
- package/lib/SimpleEventPlugin.js +54 -6
- package/lib/SyntheticClipboardEvent.js +7 -1
- package/lib/SyntheticDragEvent.js +44 -0
- package/lib/SyntheticEvent.js +2 -1
- package/lib/SyntheticKeyboardEvent.js +4 -2
- package/lib/SyntheticWheelEvent.js +10 -7
- package/lib/Transaction.js +61 -36
- package/lib/cloneWithProps.js +59 -0
- package/lib/createArrayFrom.js +10 -13
- package/lib/createFullPageComponent.js +63 -0
- package/lib/cx.js +2 -2
- package/lib/flattenChildren.js +5 -2
- package/lib/getActiveElement.js +4 -3
- package/lib/getEventKey.js +85 -0
- package/lib/getMarkupWrap.js +10 -0
- package/lib/getTextContentAccessor.js +5 -3
- package/lib/getUnboundedScrollPosition.js +2 -2
- package/lib/invariant.js +12 -4
- package/lib/isEventSupported.js +7 -11
- package/lib/mergeHelpers.js +5 -6
- package/lib/onlyChild.js +43 -0
- package/lib/shouldUpdateReactComponent.js +58 -0
- package/lib/toArray.js +75 -0
- package/lib/traverseAllChildren.js +69 -7
- package/lib/warning.js +40 -0
- package/package.json +2 -3
- package/react.js +0 -3
- package/ReactJSErrors.js +0 -40
- package/lib/$.js +0 -46
- package/lib/CallbackRegistry.js +0 -91
- package/lib/LinkedValueMixin.js +0 -68
- package/lib/ex.js +0 -49
- package/lib/filterAttributes.js +0 -45
- package/lib/ge.js +0 -76
- package/lib/mutateHTMLNodeWithMarkup.js +0 -100
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2013 Facebook, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
* @providesModule ReactDOMImg
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
"use strict";
|
|
20
|
+
|
|
21
|
+
var ReactCompositeComponent = require("./ReactCompositeComponent");
|
|
22
|
+
var ReactDOM = require("./ReactDOM");
|
|
23
|
+
var ReactEventEmitter = require("./ReactEventEmitter");
|
|
24
|
+
var EventConstants = require("./EventConstants");
|
|
25
|
+
|
|
26
|
+
// Store a reference to the <img> `ReactDOMComponent`.
|
|
27
|
+
var img = ReactDOM.img;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Since onLoad doesn't bubble OR capture on the top level in IE8, we need to
|
|
31
|
+
* capture it on the <img> element itself. There are lots of hacks we could do
|
|
32
|
+
* to accomplish this, but the most reliable is to make <img> a composite
|
|
33
|
+
* component and use `componentDidMount` to attach the event handlers.
|
|
34
|
+
*/
|
|
35
|
+
var ReactDOMImg = ReactCompositeComponent.createClass({
|
|
36
|
+
displayName: 'ReactDOMImg',
|
|
37
|
+
tagName: 'IMG',
|
|
38
|
+
|
|
39
|
+
render: function() {
|
|
40
|
+
return img(this.props);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
componentDidMount: function() {
|
|
44
|
+
var node = this.getDOMNode();
|
|
45
|
+
ReactEventEmitter.trapBubbledEvent(
|
|
46
|
+
EventConstants.topLevelTypes.topLoad,
|
|
47
|
+
'load',
|
|
48
|
+
node
|
|
49
|
+
);
|
|
50
|
+
ReactEventEmitter.trapBubbledEvent(
|
|
51
|
+
EventConstants.topLevelTypes.topError,
|
|
52
|
+
'error',
|
|
53
|
+
node
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
module.exports = ReactDOMImg;
|
package/lib/ReactDOMInput.js
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
"use strict";
|
|
20
20
|
|
|
21
|
+
var AutoFocusMixin = require("./AutoFocusMixin");
|
|
21
22
|
var DOMPropertyOperations = require("./DOMPropertyOperations");
|
|
22
|
-
var
|
|
23
|
+
var LinkedValueUtils = require("./LinkedValueUtils");
|
|
23
24
|
var ReactCompositeComponent = require("./ReactCompositeComponent");
|
|
24
25
|
var ReactDOM = require("./ReactDOM");
|
|
25
26
|
var ReactMount = require("./ReactMount");
|
|
@@ -49,7 +50,9 @@ var instancesByReactID = {};
|
|
|
49
50
|
* @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
|
|
50
51
|
*/
|
|
51
52
|
var ReactDOMInput = ReactCompositeComponent.createClass({
|
|
52
|
-
|
|
53
|
+
displayName: 'ReactDOMInput',
|
|
54
|
+
|
|
55
|
+
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
|
|
53
56
|
|
|
54
57
|
getInitialState: function() {
|
|
55
58
|
var defaultValue = this.props.defaultValue;
|
|
@@ -70,19 +73,20 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
|
|
|
70
73
|
|
|
71
74
|
props.defaultChecked = null;
|
|
72
75
|
props.defaultValue = null;
|
|
73
|
-
props.checked =
|
|
74
|
-
this.props.checked != null ? this.props.checked : this.state.checked;
|
|
75
76
|
|
|
76
|
-
var value =
|
|
77
|
+
var value = LinkedValueUtils.getValue(this);
|
|
77
78
|
props.value = value != null ? value : this.state.value;
|
|
78
79
|
|
|
80
|
+
var checked = LinkedValueUtils.getChecked(this);
|
|
81
|
+
props.checked = checked != null ? checked : this.state.checked;
|
|
82
|
+
|
|
79
83
|
props.onChange = this._handleChange;
|
|
80
84
|
|
|
81
85
|
return input(props, this.props.children);
|
|
82
86
|
},
|
|
83
87
|
|
|
84
|
-
componentDidMount: function(
|
|
85
|
-
var id = ReactMount.getID(
|
|
88
|
+
componentDidMount: function() {
|
|
89
|
+
var id = ReactMount.getID(this.getDOMNode());
|
|
86
90
|
instancesByReactID[id] = this;
|
|
87
91
|
},
|
|
88
92
|
|
|
@@ -92,7 +96,8 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
|
|
|
92
96
|
delete instancesByReactID[id];
|
|
93
97
|
},
|
|
94
98
|
|
|
95
|
-
componentDidUpdate: function(prevProps, prevState,
|
|
99
|
+
componentDidUpdate: function(prevProps, prevState, prevContext) {
|
|
100
|
+
var rootNode = this.getDOMNode();
|
|
96
101
|
if (this.props.checked != null) {
|
|
97
102
|
DOMPropertyOperations.setValueForProperty(
|
|
98
103
|
rootNode,
|
|
@@ -101,7 +106,7 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
|
|
|
101
106
|
);
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
var value =
|
|
109
|
+
var value = LinkedValueUtils.getValue(this);
|
|
105
110
|
if (value != null) {
|
|
106
111
|
// Cast `value` to a string to ensure the value is set correctly. While
|
|
107
112
|
// browsers typically do this as necessary, jsdom doesn't.
|
|
@@ -111,10 +116,10 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
|
|
|
111
116
|
|
|
112
117
|
_handleChange: function(event) {
|
|
113
118
|
var returnValue;
|
|
114
|
-
var onChange =
|
|
119
|
+
var onChange = LinkedValueUtils.getOnChange(this);
|
|
115
120
|
if (onChange) {
|
|
116
121
|
this._isChanging = true;
|
|
117
|
-
returnValue = onChange(event);
|
|
122
|
+
returnValue = onChange.call(this, event);
|
|
118
123
|
this._isChanging = false;
|
|
119
124
|
}
|
|
120
125
|
this.setState({
|
|
@@ -125,17 +130,24 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
|
|
|
125
130
|
var name = this.props.name;
|
|
126
131
|
if (this.props.type === 'radio' && name != null) {
|
|
127
132
|
var rootNode = this.getDOMNode();
|
|
133
|
+
var queryRoot = rootNode;
|
|
134
|
+
|
|
135
|
+
while (queryRoot.parentNode) {
|
|
136
|
+
queryRoot = queryRoot.parentNode;
|
|
137
|
+
}
|
|
138
|
+
|
|
128
139
|
// If `rootNode.form` was non-null, then we could try `form.elements`,
|
|
129
140
|
// but that sometimes behaves strangely in IE8. We could also try using
|
|
130
141
|
// `form.getElementsByName`, but that will only return direct children
|
|
131
142
|
// and won't include inputs that use the HTML5 `form=` attribute. Since
|
|
132
143
|
// the input might not even be in a form, let's just use the global
|
|
133
|
-
// `
|
|
134
|
-
var group =
|
|
144
|
+
// `querySelectorAll` to ensure we don't miss anything.
|
|
145
|
+
var group = queryRoot.querySelectorAll(
|
|
146
|
+
'input[name=' + JSON.stringify('' + name) + '][type="radio"]');
|
|
147
|
+
|
|
135
148
|
for (var i = 0, groupLen = group.length; i < groupLen; i++) {
|
|
136
149
|
var otherNode = group[i];
|
|
137
150
|
if (otherNode === rootNode ||
|
|
138
|
-
otherNode.nodeName !== 'INPUT' || otherNode.type !== 'radio' ||
|
|
139
151
|
otherNode.form !== rootNode.form) {
|
|
140
152
|
continue;
|
|
141
153
|
}
|
package/lib/ReactDOMOption.js
CHANGED
|
@@ -28,6 +28,7 @@ var option = ReactDOM.option;
|
|
|
28
28
|
* Implements an <option> native component that warns when `selected` is set.
|
|
29
29
|
*/
|
|
30
30
|
var ReactDOMOption = ReactCompositeComponent.createClass({
|
|
31
|
+
displayName: 'ReactDOMOption',
|
|
31
32
|
|
|
32
33
|
componentWillMount: function() {
|
|
33
34
|
// TODO (yungsters): Remove support for `selected` in <option>.
|
package/lib/ReactDOMSelect.js
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
|
|
19
19
|
"use strict";
|
|
20
20
|
|
|
21
|
-
var
|
|
21
|
+
var AutoFocusMixin = require("./AutoFocusMixin");
|
|
22
|
+
var LinkedValueUtils = require("./LinkedValueUtils");
|
|
22
23
|
var ReactCompositeComponent = require("./ReactCompositeComponent");
|
|
23
24
|
var ReactDOM = require("./ReactDOM");
|
|
24
25
|
|
|
@@ -55,19 +56,28 @@ function selectValueType(props, propName, componentName) {
|
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
58
|
* If `value` is supplied, updates <option> elements on mount and update.
|
|
59
|
+
* @param {ReactComponent} component Instance of ReactDOMSelect
|
|
60
|
+
* @param {?*} propValue For uncontrolled components, null/undefined. For
|
|
61
|
+
* controlled components, a string (or with `multiple`, a list of strings).
|
|
58
62
|
* @private
|
|
59
63
|
*/
|
|
60
|
-
function updateOptions() {
|
|
61
|
-
|
|
62
|
-
var
|
|
63
|
-
var
|
|
64
|
-
var
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
function updateOptions(component, propValue) {
|
|
65
|
+
var multiple = component.props.multiple;
|
|
66
|
+
var value = propValue != null ? propValue : component.state.value;
|
|
67
|
+
var options = component.getDOMNode().options;
|
|
68
|
+
var selectedValue, i, l;
|
|
69
|
+
if (multiple) {
|
|
70
|
+
selectedValue = {};
|
|
71
|
+
for (i = 0, l = value.length; i < l; ++i) {
|
|
72
|
+
selectedValue['' + value[i]] = true;
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
selectedValue = '' + value;
|
|
76
|
+
}
|
|
77
|
+
for (i = 0, l = options.length; i < l; i++) {
|
|
78
|
+
var selected = multiple ?
|
|
79
|
+
selectedValue.hasOwnProperty(options[i].value) :
|
|
80
|
+
options[i].value === selectedValue;
|
|
71
81
|
|
|
72
82
|
if (selected !== options[i].selected) {
|
|
73
83
|
options[i].selected = selected;
|
|
@@ -91,7 +101,9 @@ function updateOptions() {
|
|
|
91
101
|
* selected.
|
|
92
102
|
*/
|
|
93
103
|
var ReactDOMSelect = ReactCompositeComponent.createClass({
|
|
94
|
-
|
|
104
|
+
displayName: 'ReactDOMSelect',
|
|
105
|
+
|
|
106
|
+
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
|
|
95
107
|
|
|
96
108
|
propTypes: {
|
|
97
109
|
defaultValue: selectValueType,
|
|
@@ -125,16 +137,23 @@ var ReactDOMSelect = ReactCompositeComponent.createClass({
|
|
|
125
137
|
return select(props, this.props.children);
|
|
126
138
|
},
|
|
127
139
|
|
|
128
|
-
componentDidMount:
|
|
140
|
+
componentDidMount: function() {
|
|
141
|
+
updateOptions(this, LinkedValueUtils.getValue(this));
|
|
142
|
+
},
|
|
129
143
|
|
|
130
|
-
componentDidUpdate:
|
|
144
|
+
componentDidUpdate: function() {
|
|
145
|
+
var value = LinkedValueUtils.getValue(this);
|
|
146
|
+
if (value != null) {
|
|
147
|
+
updateOptions(this, value);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
131
150
|
|
|
132
151
|
_handleChange: function(event) {
|
|
133
152
|
var returnValue;
|
|
134
|
-
var onChange =
|
|
153
|
+
var onChange = LinkedValueUtils.getOnChange(this);
|
|
135
154
|
if (onChange) {
|
|
136
155
|
this._isChanging = true;
|
|
137
|
-
returnValue = onChange(event);
|
|
156
|
+
returnValue = onChange.call(this, event);
|
|
138
157
|
this._isChanging = false;
|
|
139
158
|
}
|
|
140
159
|
|
package/lib/ReactDOMTextarea.js
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
"use strict";
|
|
20
20
|
|
|
21
|
+
var AutoFocusMixin = require("./AutoFocusMixin");
|
|
21
22
|
var DOMPropertyOperations = require("./DOMPropertyOperations");
|
|
22
|
-
var
|
|
23
|
+
var LinkedValueUtils = require("./LinkedValueUtils");
|
|
23
24
|
var ReactCompositeComponent = require("./ReactCompositeComponent");
|
|
24
25
|
var ReactDOM = require("./ReactDOM");
|
|
25
26
|
|
|
@@ -45,7 +46,9 @@ var textarea = ReactDOM.textarea;
|
|
|
45
46
|
* `defaultValue` if specified, or the children content (deprecated).
|
|
46
47
|
*/
|
|
47
48
|
var ReactDOMTextarea = ReactCompositeComponent.createClass({
|
|
48
|
-
|
|
49
|
+
displayName: 'ReactDOMTextarea',
|
|
50
|
+
|
|
51
|
+
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
|
|
49
52
|
|
|
50
53
|
getInitialState: function() {
|
|
51
54
|
var defaultValue = this.props.defaultValue;
|
|
@@ -75,7 +78,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
|
|
|
75
78
|
if (defaultValue == null) {
|
|
76
79
|
defaultValue = '';
|
|
77
80
|
}
|
|
78
|
-
var value =
|
|
81
|
+
var value = LinkedValueUtils.getValue(this);
|
|
79
82
|
return {
|
|
80
83
|
// We save the initial value so that `ReactDOMComponent` doesn't update
|
|
81
84
|
// `textContent` (unnecessary since we update value).
|
|
@@ -94,7 +97,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
|
|
|
94
97
|
render: function() {
|
|
95
98
|
// Clone `this.props` so we don't mutate the input.
|
|
96
99
|
var props = merge(this.props);
|
|
97
|
-
var value =
|
|
100
|
+
var value = LinkedValueUtils.getValue(this);
|
|
98
101
|
|
|
99
102
|
("production" !== process.env.NODE_ENV ? invariant(
|
|
100
103
|
props.dangerouslySetInnerHTML == null,
|
|
@@ -110,9 +113,10 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
|
|
|
110
113
|
return textarea(props, this.state.initialValue);
|
|
111
114
|
},
|
|
112
115
|
|
|
113
|
-
componentDidUpdate: function(prevProps, prevState,
|
|
114
|
-
var value =
|
|
116
|
+
componentDidUpdate: function(prevProps, prevState, prevContext) {
|
|
117
|
+
var value = LinkedValueUtils.getValue(this);
|
|
115
118
|
if (value != null) {
|
|
119
|
+
var rootNode = this.getDOMNode();
|
|
116
120
|
// Cast `value` to a string to ensure the value is set correctly. While
|
|
117
121
|
// browsers typically do this as necessary, jsdom doesn't.
|
|
118
122
|
DOMPropertyOperations.setValueForProperty(rootNode, 'value', '' + value);
|
|
@@ -121,10 +125,10 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
|
|
|
121
125
|
|
|
122
126
|
_handleChange: function(event) {
|
|
123
127
|
var returnValue;
|
|
124
|
-
var onChange =
|
|
128
|
+
var onChange = LinkedValueUtils.getOnChange(this);
|
|
125
129
|
if (onChange) {
|
|
126
130
|
this._isChanging = true;
|
|
127
|
-
returnValue = onChange(event);
|
|
131
|
+
returnValue = onChange.call(this, event);
|
|
128
132
|
this._isChanging = false;
|
|
129
133
|
}
|
|
130
134
|
this.setState({value: event.target.value});
|
|
@@ -18,46 +18,54 @@
|
|
|
18
18
|
|
|
19
19
|
"use strict";
|
|
20
20
|
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
var
|
|
24
|
-
var ReactDOMInput = require("./ReactDOMInput");
|
|
25
|
-
var ReactDOMOption = require("./ReactDOMOption");
|
|
26
|
-
var ReactDOMSelect = require("./ReactDOMSelect");
|
|
27
|
-
var ReactDOMTextarea = require("./ReactDOMTextarea");
|
|
28
|
-
var ReactEventEmitter = require("./ReactEventEmitter");
|
|
29
|
-
var ReactEventTopLevelCallback = require("./ReactEventTopLevelCallback");
|
|
30
|
-
var ReactPerf = require("./ReactPerf");
|
|
21
|
+
var ReactInjection = require("./ReactInjection");
|
|
22
|
+
|
|
23
|
+
var ExecutionEnvironment = require("./ExecutionEnvironment");
|
|
31
24
|
|
|
32
25
|
var DefaultDOMPropertyConfig = require("./DefaultDOMPropertyConfig");
|
|
33
|
-
var DOMProperty = require("./DOMProperty");
|
|
34
26
|
|
|
35
27
|
var ChangeEventPlugin = require("./ChangeEventPlugin");
|
|
28
|
+
var ClientReactRootIndex = require("./ClientReactRootIndex");
|
|
36
29
|
var CompositionEventPlugin = require("./CompositionEventPlugin");
|
|
37
30
|
var DefaultEventPluginOrder = require("./DefaultEventPluginOrder");
|
|
38
31
|
var EnterLeaveEventPlugin = require("./EnterLeaveEventPlugin");
|
|
39
|
-
var EventPluginHub = require("./EventPluginHub");
|
|
40
32
|
var MobileSafariClickEventPlugin = require("./MobileSafariClickEventPlugin");
|
|
33
|
+
var ReactEventTopLevelCallback = require("./ReactEventTopLevelCallback");
|
|
34
|
+
var ReactDOM = require("./ReactDOM");
|
|
35
|
+
var ReactDOMButton = require("./ReactDOMButton");
|
|
36
|
+
var ReactDOMForm = require("./ReactDOMForm");
|
|
37
|
+
var ReactDOMImg = require("./ReactDOMImg");
|
|
38
|
+
var ReactDOMInput = require("./ReactDOMInput");
|
|
39
|
+
var ReactDOMOption = require("./ReactDOMOption");
|
|
40
|
+
var ReactDOMSelect = require("./ReactDOMSelect");
|
|
41
|
+
var ReactDOMTextarea = require("./ReactDOMTextarea");
|
|
41
42
|
var ReactInstanceHandles = require("./ReactInstanceHandles");
|
|
43
|
+
var ReactMount = require("./ReactMount");
|
|
42
44
|
var SelectEventPlugin = require("./SelectEventPlugin");
|
|
45
|
+
var ServerReactRootIndex = require("./ServerReactRootIndex");
|
|
43
46
|
var SimpleEventPlugin = require("./SimpleEventPlugin");
|
|
44
47
|
|
|
45
48
|
var ReactDefaultBatchingStrategy = require("./ReactDefaultBatchingStrategy");
|
|
46
|
-
|
|
49
|
+
|
|
50
|
+
var createFullPageComponent = require("./createFullPageComponent");
|
|
47
51
|
|
|
48
52
|
function inject() {
|
|
49
|
-
|
|
53
|
+
ReactInjection.EventEmitter.injectTopLevelCallbackCreator(
|
|
54
|
+
ReactEventTopLevelCallback
|
|
55
|
+
);
|
|
56
|
+
|
|
50
57
|
/**
|
|
51
|
-
* Inject
|
|
58
|
+
* Inject modules for resolving DOM hierarchy and plugin ordering.
|
|
52
59
|
*/
|
|
53
|
-
EventPluginHub.
|
|
54
|
-
EventPluginHub.
|
|
60
|
+
ReactInjection.EventPluginHub.injectEventPluginOrder(DefaultEventPluginOrder);
|
|
61
|
+
ReactInjection.EventPluginHub.injectInstanceHandle(ReactInstanceHandles);
|
|
62
|
+
ReactInjection.EventPluginHub.injectMount(ReactMount);
|
|
55
63
|
|
|
56
64
|
/**
|
|
57
65
|
* Some important event plugins included by default (without having to require
|
|
58
66
|
* them).
|
|
59
67
|
*/
|
|
60
|
-
EventPluginHub.
|
|
68
|
+
ReactInjection.EventPluginHub.injectEventPluginsByName({
|
|
61
69
|
SimpleEventPlugin: SimpleEventPlugin,
|
|
62
70
|
EnterLeaveEventPlugin: EnterLeaveEventPlugin,
|
|
63
71
|
ChangeEventPlugin: ChangeEventPlugin,
|
|
@@ -66,24 +74,40 @@ function inject() {
|
|
|
66
74
|
SelectEventPlugin: SelectEventPlugin
|
|
67
75
|
});
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
ReactInjection.DOM.injectComponentClasses({
|
|
70
78
|
button: ReactDOMButton,
|
|
71
79
|
form: ReactDOMForm,
|
|
80
|
+
img: ReactDOMImg,
|
|
72
81
|
input: ReactDOMInput,
|
|
73
82
|
option: ReactDOMOption,
|
|
74
83
|
select: ReactDOMSelect,
|
|
75
|
-
textarea: ReactDOMTextarea
|
|
76
|
-
});
|
|
84
|
+
textarea: ReactDOMTextarea,
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
html: createFullPageComponent(ReactDOM.html),
|
|
87
|
+
head: createFullPageComponent(ReactDOM.head),
|
|
88
|
+
title: createFullPageComponent(ReactDOM.title),
|
|
89
|
+
body: createFullPageComponent(ReactDOM.body)
|
|
90
|
+
});
|
|
79
91
|
|
|
80
|
-
|
|
81
|
-
ReactPerf.injection.injectMeasure(require("./ReactDefaultPerf").measure);
|
|
82
|
-
}
|
|
92
|
+
ReactInjection.DOMProperty.injectDOMPropertyConfig(DefaultDOMPropertyConfig);
|
|
83
93
|
|
|
84
|
-
|
|
94
|
+
ReactInjection.Updates.injectBatchingStrategy(
|
|
85
95
|
ReactDefaultBatchingStrategy
|
|
86
96
|
);
|
|
97
|
+
|
|
98
|
+
ReactInjection.RootIndex.injectCreateReactRootIndex(
|
|
99
|
+
ExecutionEnvironment.canUseDOM ?
|
|
100
|
+
ClientReactRootIndex.createReactRootIndex :
|
|
101
|
+
ServerReactRootIndex.createReactRootIndex
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if ("production" !== process.env.NODE_ENV) {
|
|
105
|
+
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
|
|
106
|
+
if ((/[?&]react_perf\b/).test(url)) {
|
|
107
|
+
var ReactDefaultPerf = require("./ReactDefaultPerf");
|
|
108
|
+
ReactDefaultPerf.start();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
87
111
|
}
|
|
88
112
|
|
|
89
113
|
module.exports = {
|
package/lib/ReactDefaultPerf.js
CHANGED
|
@@ -19,389 +19,226 @@
|
|
|
19
19
|
|
|
20
20
|
"use strict";
|
|
21
21
|
|
|
22
|
+
var DOMProperty = require("./DOMProperty");
|
|
23
|
+
var ReactDefaultPerfAnalysis = require("./ReactDefaultPerfAnalysis");
|
|
24
|
+
var ReactMount = require("./ReactMount");
|
|
25
|
+
var ReactPerf = require("./ReactPerf");
|
|
26
|
+
|
|
22
27
|
var performanceNow = require("./performanceNow");
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
function roundFloat(val) {
|
|
30
|
+
return Math.floor(val * 100) / 100;
|
|
31
|
+
}
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* Gets the stored information for a given object's function.
|
|
30
|
-
*
|
|
31
|
-
* @param {string} objName
|
|
32
|
-
* @param {string} fnName
|
|
33
|
-
* @return {?object}
|
|
34
|
-
*/
|
|
35
|
-
getInfo: function(objName, fnName) {
|
|
36
|
-
if (!this.info[objName] || !this.info[objName][fnName]) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
return this.info[objName][fnName];
|
|
40
|
-
},
|
|
33
|
+
var ReactDefaultPerf = {
|
|
34
|
+
_allMeasurements: [], // last item in the list is the current one
|
|
35
|
+
_injected: false,
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* @param {string} fnName
|
|
47
|
-
* @return {?array<object>}
|
|
48
|
-
*/
|
|
49
|
-
getLogs: function(objName, fnName) {
|
|
50
|
-
if (!this.getInfo(objName, fnName)) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
return this.logs.filter(function(log) {
|
|
54
|
-
return log.objName === objName && log.fnName === fnName;
|
|
55
|
-
});
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Runs through the logs and builds an array of arrays, where each array
|
|
60
|
-
* walks through the mounting/updating of each component underneath.
|
|
61
|
-
*
|
|
62
|
-
* @param {string} rootID The reactID of the root node, e.g. '.r[2cpyq]'
|
|
63
|
-
* @return {array<array>}
|
|
64
|
-
*/
|
|
65
|
-
getRawRenderHistory: function(rootID) {
|
|
66
|
-
var history = [];
|
|
67
|
-
/**
|
|
68
|
-
* Since logs are added after the method returns, the logs are in a sense
|
|
69
|
-
* upside-down: the inner-most elements from mounting/updating are logged
|
|
70
|
-
* first, and the last addition to the log is the top renderComponent.
|
|
71
|
-
* Therefore, we flip the logs upside down for ease of processing, and
|
|
72
|
-
* reverse the history array at the end so the earliest event has index 0.
|
|
73
|
-
*/
|
|
74
|
-
var logs = this.logs.filter(function(log) {
|
|
75
|
-
return log.reactID.indexOf(rootID) === 0;
|
|
76
|
-
}).reverse();
|
|
37
|
+
start: function() {
|
|
38
|
+
if (!ReactDefaultPerf._injected) {
|
|
39
|
+
ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure);
|
|
40
|
+
}
|
|
77
41
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
42
|
+
ReactDefaultPerf._allMeasurements.length = 0;
|
|
43
|
+
ReactPerf.enableMeasure = true;
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
stop: function() {
|
|
47
|
+
ReactPerf.enableMeasure = false;
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
getLastMeasurements: function() {
|
|
51
|
+
return ReactDefaultPerf._allMeasurements;
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
printExclusive: function(measurements) {
|
|
55
|
+
measurements = measurements || ReactDefaultPerf._allMeasurements;
|
|
56
|
+
var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements);
|
|
57
|
+
console.table(summary.map(function(item) {
|
|
58
|
+
return {
|
|
59
|
+
'Component class name': item.componentName,
|
|
60
|
+
'Total inclusive time (ms)': roundFloat(item.inclusive),
|
|
61
|
+
'Total exclusive time (ms)': roundFloat(item.exclusive),
|
|
62
|
+
'Exclusive time per instance (ms)': roundFloat(item.exclusive / item.count),
|
|
63
|
+
'Instances': item.count
|
|
64
|
+
};
|
|
65
|
+
}));
|
|
66
|
+
console.log(
|
|
67
|
+
'Total time:',
|
|
68
|
+
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
printInclusive: function(measurements) {
|
|
73
|
+
measurements = measurements || ReactDefaultPerf._allMeasurements;
|
|
74
|
+
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements);
|
|
75
|
+
console.table(summary.map(function(item) {
|
|
76
|
+
return {
|
|
77
|
+
'Owner > component': item.componentName,
|
|
78
|
+
'Inclusive time (ms)': roundFloat(item.time),
|
|
79
|
+
'Instances': item.count
|
|
80
|
+
};
|
|
81
|
+
}));
|
|
82
|
+
console.log(
|
|
83
|
+
'Total time:',
|
|
84
|
+
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
printWasted: function(measurements) {
|
|
89
|
+
measurements = measurements || ReactDefaultPerf._allMeasurements;
|
|
90
|
+
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(
|
|
91
|
+
measurements,
|
|
92
|
+
true
|
|
93
|
+
);
|
|
94
|
+
console.table(summary.map(function(item) {
|
|
95
|
+
return {
|
|
96
|
+
'Owner > component': item.componentName,
|
|
97
|
+
'Wasted time (ms)': item.time,
|
|
98
|
+
'Instances': item.count
|
|
99
|
+
};
|
|
100
|
+
}));
|
|
101
|
+
console.log(
|
|
102
|
+
'Total time:',
|
|
103
|
+
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
printDOM: function(measurements) {
|
|
108
|
+
measurements = measurements || ReactDefaultPerf._allMeasurements;
|
|
109
|
+
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
|
|
110
|
+
console.table(summary.map(function(item) {
|
|
111
|
+
var result = {};
|
|
112
|
+
result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id;
|
|
113
|
+
result['type'] = item.type;
|
|
114
|
+
result['args'] = JSON.stringify(item.args);
|
|
115
|
+
return result;
|
|
116
|
+
}));
|
|
117
|
+
console.log(
|
|
118
|
+
'Total time:',
|
|
119
|
+
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
|
|
120
|
+
);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
_recordWrite: function(id, fnName, totalTime, args) {
|
|
124
|
+
// TODO: totalTime isn't that useful since it doesn't count paints/reflows
|
|
125
|
+
var writes =
|
|
126
|
+
ReactDefaultPerf
|
|
127
|
+
._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1]
|
|
128
|
+
.writes;
|
|
129
|
+
writes[id] = writes[id] || [];
|
|
130
|
+
writes[id].push({
|
|
131
|
+
type: fnName,
|
|
132
|
+
time: totalTime,
|
|
133
|
+
args: args
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
measure: function(moduleName, fnName, func) {
|
|
138
|
+
return function() {var args=Array.prototype.slice.call(arguments,0);
|
|
139
|
+
var totalTime;
|
|
140
|
+
var rv;
|
|
141
|
+
var start;
|
|
142
|
+
|
|
143
|
+
if (fnName === '_renderNewRootComponent' ||
|
|
144
|
+
fnName === 'flushBatchedUpdates') {
|
|
145
|
+
// A "measurement" is a set of metrics recorded for each flush. We want
|
|
146
|
+
// to group the metrics for a given flush together so we can look at the
|
|
147
|
+
// components that rendered and the DOM operations that actually
|
|
148
|
+
// happened to determine the amount of "wasted work" performed.
|
|
149
|
+
ReactDefaultPerf._allMeasurements.push({
|
|
150
|
+
exclusive: {},
|
|
151
|
+
inclusive: {},
|
|
152
|
+
counts: {},
|
|
153
|
+
writes: {},
|
|
154
|
+
displayNames: {},
|
|
155
|
+
totalTime: 0
|
|
156
|
+
});
|
|
157
|
+
start = performanceNow();
|
|
158
|
+
rv = func.apply(this, args);
|
|
159
|
+
ReactDefaultPerf._allMeasurements[
|
|
160
|
+
ReactDefaultPerf._allMeasurements.length - 1
|
|
161
|
+
].totalTime = performanceNow() - start;
|
|
162
|
+
return rv;
|
|
163
|
+
} else if (moduleName === 'ReactDOMIDOperations' ||
|
|
164
|
+
moduleName === 'ReactComponentBrowserEnvironment') {
|
|
165
|
+
start = performanceNow();
|
|
166
|
+
rv = func.apply(this, args);
|
|
167
|
+
totalTime = performanceNow() - start;
|
|
168
|
+
|
|
169
|
+
if (fnName === 'mountImageIntoNode') {
|
|
170
|
+
var mountID = ReactMount.getID(args[1]);
|
|
171
|
+
ReactDefaultPerf._recordWrite(mountID, fnName, totalTime, args[0]);
|
|
172
|
+
} else if (fnName === 'dangerouslyProcessChildrenUpdates') {
|
|
173
|
+
// special format
|
|
174
|
+
args[0].forEach(function(update) {
|
|
175
|
+
var writeArgs = {};
|
|
176
|
+
if (update.fromIndex !== null) {
|
|
177
|
+
writeArgs.fromIndex = update.fromIndex;
|
|
178
|
+
}
|
|
179
|
+
if (update.toIndex !== null) {
|
|
180
|
+
writeArgs.toIndex = update.toIndex;
|
|
181
|
+
}
|
|
182
|
+
if (update.textContent !== null) {
|
|
183
|
+
writeArgs.textContent = update.textContent;
|
|
184
|
+
}
|
|
185
|
+
if (update.markupIndex !== null) {
|
|
186
|
+
writeArgs.markup = args[1][update.markupIndex];
|
|
187
|
+
}
|
|
188
|
+
ReactDefaultPerf._recordWrite(
|
|
189
|
+
update.parentID,
|
|
190
|
+
update.type,
|
|
191
|
+
totalTime,
|
|
192
|
+
writeArgs
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
} else {
|
|
196
|
+
// basic format
|
|
197
|
+
ReactDefaultPerf._recordWrite(
|
|
198
|
+
args[0],
|
|
199
|
+
fnName,
|
|
200
|
+
totalTime,
|
|
201
|
+
Array.prototype.slice.call(args, 1)
|
|
202
|
+
);
|
|
83
203
|
}
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
var history = this.getRawRenderHistory(rootID);
|
|
102
|
-
|
|
103
|
-
return history.map(function(subHistory) {
|
|
104
|
-
var headerString = (
|
|
105
|
-
'log# Component (execution time) [bloat from logging]\n' +
|
|
106
|
-
'================================================================\n'
|
|
107
|
-
);
|
|
108
|
-
return headerString + subHistory.map(function(log) {
|
|
109
|
-
// Add two spaces for every layer in the reactID.
|
|
110
|
-
var indents = '\t' + Array(log.reactID.split('.[').length).join(' ');
|
|
111
|
-
var delta = _microTime(log.timing.delta);
|
|
112
|
-
var bloat = _microTime(log.timing.timeToLog);
|
|
113
|
-
|
|
114
|
-
return log.index + indents + log.name + ' (' + delta + 'ms)' +
|
|
115
|
-
' [' + bloat + 'ms]';
|
|
116
|
-
}).join('\n');
|
|
117
|
-
});
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Print the render history from `getRenderHistory` using console.log.
|
|
122
|
-
* This is currently the best way to display perf data from
|
|
123
|
-
* any React component; working on that.
|
|
124
|
-
*
|
|
125
|
-
* @param {string} rootID The reactID of the root node, e.g. '.r[2cpyq]'
|
|
126
|
-
* @param {number} index
|
|
127
|
-
*/
|
|
128
|
-
printRenderHistory: function(rootID, index) {
|
|
129
|
-
var history = this.getRenderHistory(rootID);
|
|
130
|
-
if (!history[index]) {
|
|
131
|
-
console.warn(
|
|
132
|
-
'Index', index, 'isn\'t available! ' +
|
|
133
|
-
'The render history is', history.length, 'long.'
|
|
134
|
-
);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
console.log(
|
|
138
|
-
'Loading render history #' + (index + 1) +
|
|
139
|
-
' of ' + history.length + ':\n' + history[index]
|
|
140
|
-
);
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Prints the heatmap legend to console, showing how the colors correspond
|
|
145
|
-
* with render times. This relies on console.log styles.
|
|
146
|
-
*/
|
|
147
|
-
printHeatmapLegend: function() {
|
|
148
|
-
if (!this.options.heatmap.enabled) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
var max = this.info.React
|
|
152
|
-
&& this.info.React.renderComponent
|
|
153
|
-
&& this.info.React.renderComponent.max;
|
|
154
|
-
if (max) {
|
|
155
|
-
var logStr = 'Heatmap: ';
|
|
156
|
-
for (var ii = 0; ii <= 10 * max; ii += max) {
|
|
157
|
-
logStr += '%c ' + (Math.round(ii) / 10) + 'ms ';
|
|
204
|
+
return rv;
|
|
205
|
+
} else if (moduleName === 'ReactCompositeComponent' && (
|
|
206
|
+
fnName === 'mountComponent' ||
|
|
207
|
+
fnName === 'updateComponent' || // TODO: receiveComponent()?
|
|
208
|
+
fnName === '_renderValidatedComponent')) {
|
|
209
|
+
|
|
210
|
+
var rootNodeID = fnName === 'mountComponent' ?
|
|
211
|
+
args[0] :
|
|
212
|
+
this._rootNodeID;
|
|
213
|
+
var isRender = fnName === '_renderValidatedComponent';
|
|
214
|
+
var entry = ReactDefaultPerf._allMeasurements[
|
|
215
|
+
ReactDefaultPerf._allMeasurements.length - 1
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
if (isRender) {
|
|
219
|
+
entry.counts[rootNodeID] = entry.counts[rootNodeID] || 0;
|
|
220
|
+
entry.counts[rootNodeID] += 1;
|
|
158
221
|
}
|
|
159
|
-
console.log(
|
|
160
|
-
logStr,
|
|
161
|
-
'background-color: hsla(100, 100%, 50%, 0.6);',
|
|
162
|
-
'background-color: hsla( 90, 100%, 50%, 0.6);',
|
|
163
|
-
'background-color: hsla( 80, 100%, 50%, 0.6);',
|
|
164
|
-
'background-color: hsla( 70, 100%, 50%, 0.6);',
|
|
165
|
-
'background-color: hsla( 60, 100%, 50%, 0.6);',
|
|
166
|
-
'background-color: hsla( 50, 100%, 50%, 0.6);',
|
|
167
|
-
'background-color: hsla( 40, 100%, 50%, 0.6);',
|
|
168
|
-
'background-color: hsla( 30, 100%, 50%, 0.6);',
|
|
169
|
-
'background-color: hsla( 20, 100%, 50%, 0.6);',
|
|
170
|
-
'background-color: hsla( 10, 100%, 50%, 0.6);',
|
|
171
|
-
'background-color: hsla( 0, 100%, 50%, 0.6);'
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Measure a given function with logging information, and calls a callback
|
|
178
|
-
* if there is one.
|
|
179
|
-
*
|
|
180
|
-
* @param {string} objName
|
|
181
|
-
* @param {string} fnName
|
|
182
|
-
* @param {function} func
|
|
183
|
-
* @return {function}
|
|
184
|
-
*/
|
|
185
|
-
measure: function(objName, fnName, func) {
|
|
186
|
-
var info = _getNewInfo(objName, fnName);
|
|
187
222
|
|
|
188
|
-
|
|
223
|
+
start = performanceNow();
|
|
224
|
+
rv = func.apply(this, args);
|
|
225
|
+
totalTime = performanceNow() - start;
|
|
189
226
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
var timeAfterFn = performanceNow();
|
|
227
|
+
var typeOfLog = isRender ? entry.exclusive : entry.inclusive;
|
|
228
|
+
typeOfLog[rootNodeID] = typeOfLog[rootNodeID] || 0;
|
|
229
|
+
typeOfLog[rootNodeID] += totalTime;
|
|
194
230
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
* argument in the log, do so in the callback.
|
|
199
|
-
*/
|
|
200
|
-
var args = {};
|
|
201
|
-
for (var i = 0; i < arguments.length; i++) {
|
|
202
|
-
args[fnArgs[i]] = arguments[i];
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
var log = {
|
|
206
|
-
index: ReactDefaultPerf.logs.length,
|
|
207
|
-
fnName: fnName,
|
|
208
|
-
objName: objName,
|
|
209
|
-
timing: {
|
|
210
|
-
before: timeBeforeFn,
|
|
211
|
-
after: timeAfterFn,
|
|
212
|
-
delta: timeAfterFn - timeBeforeFn
|
|
213
|
-
}
|
|
231
|
+
entry.displayNames[rootNodeID] = {
|
|
232
|
+
current: this.constructor.displayName,
|
|
233
|
+
owner: this._owner ? this._owner.constructor.displayName : '<root>'
|
|
214
234
|
};
|
|
215
235
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* The callback gets:
|
|
220
|
-
* - this (the component)
|
|
221
|
-
* - the original method's arguments
|
|
222
|
-
* - what the method returned
|
|
223
|
-
* - the log object, and
|
|
224
|
-
* - the wrapped method's info object.
|
|
225
|
-
*/
|
|
226
|
-
var callback = _getCallback(objName, fnName);
|
|
227
|
-
callback && callback(this, args, fnReturn, log, info);
|
|
228
|
-
|
|
229
|
-
log.timing.timeToLog = performanceNow() - timeAfterFn;
|
|
230
|
-
|
|
231
|
-
return fnReturn;
|
|
232
|
-
};
|
|
233
|
-
},
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Holds information on wrapped objects/methods.
|
|
237
|
-
* For instance, ReactDefaultPerf.info.React.renderComponent
|
|
238
|
-
*/
|
|
239
|
-
info: {},
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Holds all of the logs. Filter this to pull desired information.
|
|
243
|
-
*/
|
|
244
|
-
logs: [],
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Toggle settings for ReactDefaultPerf
|
|
248
|
-
*/
|
|
249
|
-
options: {
|
|
250
|
-
/**
|
|
251
|
-
* The heatmap sets the background color of the React containers
|
|
252
|
-
* according to how much total time has been spent rendering them.
|
|
253
|
-
* The most temporally expensive component is set as pure red,
|
|
254
|
-
* and the others are colored from green to red as a fraction
|
|
255
|
-
* of that max component time.
|
|
256
|
-
*/
|
|
257
|
-
heatmap: {
|
|
258
|
-
enabled: true
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Gets a info area for a given object's function, adding a new one if
|
|
265
|
-
* necessary.
|
|
266
|
-
*
|
|
267
|
-
* @param {string} objName
|
|
268
|
-
* @param {string} fnName
|
|
269
|
-
* @return {object}
|
|
270
|
-
*/
|
|
271
|
-
var _getNewInfo = function(objName, fnName) {
|
|
272
|
-
var info = ReactDefaultPerf.getInfo(objName, fnName);
|
|
273
|
-
if (info) {
|
|
274
|
-
return info;
|
|
275
|
-
}
|
|
276
|
-
ReactDefaultPerf.info[objName] = ReactDefaultPerf.info[objName] || {};
|
|
277
|
-
|
|
278
|
-
return ReactDefaultPerf.info[objName][fnName] = {
|
|
279
|
-
getLogs: function() {
|
|
280
|
-
return ReactDefaultPerf.getLogs(objName, fnName);
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Gets a list of the argument names from a function's definition.
|
|
287
|
-
* This is useful for storing arguments by their names within wrapFn().
|
|
288
|
-
*
|
|
289
|
-
* @param {function} fn
|
|
290
|
-
* @return {array<string>}
|
|
291
|
-
*/
|
|
292
|
-
var _getFnArguments = function(fn) {
|
|
293
|
-
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
|
294
|
-
var fnStr = fn.toString().replace(STRIP_COMMENTS, '');
|
|
295
|
-
fnStr = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')'));
|
|
296
|
-
return fnStr.match(/([^\s,]+)/g);
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Store common callbacks within ReactDefaultPerf.
|
|
301
|
-
*
|
|
302
|
-
* @param {string} objName
|
|
303
|
-
* @param {string} fnName
|
|
304
|
-
* @return {?function}
|
|
305
|
-
*/
|
|
306
|
-
var _getCallback = function(objName, fnName) {
|
|
307
|
-
switch (objName + '.' + fnName) {
|
|
308
|
-
case 'React.renderComponent':
|
|
309
|
-
return _renderComponentCallback;
|
|
310
|
-
case 'ReactDOMComponent.mountComponent':
|
|
311
|
-
case 'ReactDOMComponent.updateComponent':
|
|
312
|
-
return _nativeComponentCallback;
|
|
313
|
-
case 'ReactCompositeComponent.mountComponent':
|
|
314
|
-
case 'ReactCompositeComponent.updateComponent':
|
|
315
|
-
return _compositeComponentCallback;
|
|
316
|
-
default:
|
|
317
|
-
return null;
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Callback function for React.renderComponent
|
|
323
|
-
*
|
|
324
|
-
* @param {object} component
|
|
325
|
-
* @param {object} args
|
|
326
|
-
* @param {?object} fnReturn
|
|
327
|
-
* @param {object} log
|
|
328
|
-
* @param {object} info
|
|
329
|
-
*/
|
|
330
|
-
var _renderComponentCallback =
|
|
331
|
-
function(component, args, fnReturn, log, info) {
|
|
332
|
-
log.name = args.nextComponent.constructor.displayName || '[unknown]';
|
|
333
|
-
log.reactID = fnReturn._rootNodeID || null;
|
|
334
|
-
|
|
335
|
-
if (ReactDefaultPerf.options.heatmap.enabled) {
|
|
336
|
-
var container = args.container;
|
|
337
|
-
if (!container.loggedByReactDefaultPerf) {
|
|
338
|
-
container.loggedByReactDefaultPerf = true;
|
|
339
|
-
info.components = info.components || [];
|
|
340
|
-
info.components.push(container);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
container.count = container.count || 0;
|
|
344
|
-
container.count += log.timing.delta;
|
|
345
|
-
info.max = info.max || 0;
|
|
346
|
-
if (container.count > info.max) {
|
|
347
|
-
info.max = container.count;
|
|
348
|
-
info.components.forEach(function(component) {
|
|
349
|
-
_setHue(component, 100 - 100 * component.count / info.max);
|
|
350
|
-
});
|
|
236
|
+
return rv;
|
|
351
237
|
} else {
|
|
352
|
-
|
|
238
|
+
return func.apply(this, args);
|
|
353
239
|
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Callback function for ReactDOMComponent
|
|
359
|
-
*
|
|
360
|
-
* @param {object} component
|
|
361
|
-
* @param {object} args
|
|
362
|
-
* @param {?object} fnReturn
|
|
363
|
-
* @param {object} log
|
|
364
|
-
* @param {object} info
|
|
365
|
-
*/
|
|
366
|
-
var _nativeComponentCallback =
|
|
367
|
-
function(component, args, fnReturn, log, info) {
|
|
368
|
-
log.name = component.tagName || '[unknown]';
|
|
369
|
-
log.reactID = component._rootNodeID;
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Callback function for ReactCompositeComponent
|
|
374
|
-
*
|
|
375
|
-
* @param {object} component
|
|
376
|
-
* @param {object} args
|
|
377
|
-
* @param {?object} fnReturn
|
|
378
|
-
* @param {object} log
|
|
379
|
-
* @param {object} info
|
|
380
|
-
*/
|
|
381
|
-
var _compositeComponentCallback =
|
|
382
|
-
function(component, args, fnReturn, log, info) {
|
|
383
|
-
log.name = component.constructor.displayName || '[unknown]';
|
|
384
|
-
log.reactID = component._rootNodeID;
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Using the hsl() background-color attribute, colors an element.
|
|
389
|
-
*
|
|
390
|
-
* @param {DOMElement} el
|
|
391
|
-
* @param {number} hue [0 for red, 120 for green, 240 for blue]
|
|
392
|
-
*/
|
|
393
|
-
var _setHue = function(el, hue) {
|
|
394
|
-
el.style.backgroundColor = 'hsla(' + hue + ', 100%, 50%, 0.6)';
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Round to the thousandth place.
|
|
399
|
-
* @param {number} time
|
|
400
|
-
* @return {number}
|
|
401
|
-
*/
|
|
402
|
-
var _microTime = function(time) {
|
|
403
|
-
return Math.round(time * 1000) / 1000;
|
|
404
|
-
};
|
|
405
|
-
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
};
|
|
406
243
|
|
|
407
244
|
module.exports = ReactDefaultPerf;
|