mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Perf: Insert nodes top-down in IE and Edge
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
'use strict';
|
||||
|
||||
var ClientReactRootIndex = require('ClientReactRootIndex');
|
||||
var DOMLazyTree = require('DOMLazyTree');
|
||||
var DOMProperty = require('DOMProperty');
|
||||
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
@@ -1038,7 +1039,7 @@ var ReactMount = {
|
||||
while (container.lastChild) {
|
||||
container.removeChild(container.lastChild);
|
||||
}
|
||||
container.appendChild(markup);
|
||||
DOMLazyTree.insertTreeBefore(container, markup, null);
|
||||
} else {
|
||||
setInnerHTML(container, markup);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var DOMLazyTree = require('DOMLazyTree');
|
||||
var Danger = require('Danger');
|
||||
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
@@ -29,21 +30,27 @@ var invariant = require('invariant');
|
||||
* @internal
|
||||
*/
|
||||
function insertChildAt(parentNode, childNode, index) {
|
||||
// By exploiting arrays returning `undefined` for an undefined index, we can
|
||||
// rely exclusively on `insertBefore(node, null)` instead of also using
|
||||
// `appendChild(node)`. However, using `undefined` is not allowed by all
|
||||
// browsers so we must replace it with `null`.
|
||||
// We can rely exclusively on `insertBefore(node, null)` instead of also using
|
||||
// `appendChild(node)`. (Using `undefined` is not allowed by all browsers so
|
||||
// we are careful to use `null`.)
|
||||
|
||||
// fix render order error in safari
|
||||
// IE8 will throw error when index out of list size.
|
||||
var beforeChild = index >= parentNode.childNodes.length ?
|
||||
null :
|
||||
parentNode.childNodes.item(index);
|
||||
// In Safari, .childNodes[index] can return a DOM node with id={index} so we
|
||||
// use .item() instead which is immune to this bug. (See #3560.) In contrast
|
||||
// to the spec, IE8 throws an error if index is larger than the list size.
|
||||
var referenceNode =
|
||||
index < parentNode.childNodes.length ?
|
||||
parentNode.childNodes.item(index) : null;
|
||||
|
||||
parentNode.insertBefore(
|
||||
childNode,
|
||||
beforeChild
|
||||
);
|
||||
parentNode.insertBefore(childNode, referenceNode);
|
||||
}
|
||||
|
||||
function insertLazyTreeChildAt(parentNode, childTree, index) {
|
||||
// See above.
|
||||
var referenceNode =
|
||||
index < parentNode.childNodes.length ?
|
||||
parentNode.childNodes.item(index) : null;
|
||||
|
||||
DOMLazyTree.insertTreeBefore(parentNode, childTree, referenceNode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,9 +106,10 @@ var DOMChildrenOperations = {
|
||||
}
|
||||
}
|
||||
|
||||
var renderedMarkup;
|
||||
// markupList is either a list of markup or just a list of elements
|
||||
if (markupList.length && typeof markupList[0] === 'string') {
|
||||
var isHTML = markupList.length && typeof markupList[0] === 'string';
|
||||
var renderedMarkup;
|
||||
if (isHTML) {
|
||||
renderedMarkup = Danger.dangerouslyRenderMarkup(markupList);
|
||||
} else {
|
||||
renderedMarkup = markupList;
|
||||
@@ -118,11 +126,19 @@ var DOMChildrenOperations = {
|
||||
update = updates[k];
|
||||
switch (update.type) {
|
||||
case ReactMultiChildUpdateTypes.INSERT_MARKUP:
|
||||
insertChildAt(
|
||||
update.parentNode,
|
||||
renderedMarkup[update.markupIndex],
|
||||
update.toIndex
|
||||
);
|
||||
if (isHTML) {
|
||||
insertChildAt(
|
||||
update.parentNode,
|
||||
renderedMarkup[update.markupIndex],
|
||||
update.toIndex
|
||||
);
|
||||
} else {
|
||||
insertLazyTreeChildAt(
|
||||
update.parentNode,
|
||||
renderedMarkup[update.markupIndex],
|
||||
update.toIndex
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
|
||||
insertChildAt(
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule DOMLazyTree
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* In IE (8-11) and Edge, appending nodes with no children is dramatically
|
||||
* faster than appending a full subtree, so we essentially queue up the
|
||||
* .appendChild calls here and apply them so each node is added to its parent
|
||||
* before any children are added.
|
||||
*
|
||||
* In other browsers, doing so is slower or neutral compared to the other order
|
||||
* (in Firefox, twice as slow) so we only do this inversion in IE.
|
||||
*
|
||||
* See https://github.com/spicyj/innerhtml-vs-createelement-vs-clonenode.
|
||||
*/
|
||||
var enableLazy = (
|
||||
typeof document !== 'undefined' &&
|
||||
typeof document.documentMode === 'number'
|
||||
||
|
||||
typeof navigator !== 'undefined' &&
|
||||
typeof navigator.userAgent === 'string' &&
|
||||
/\bEdge\/\d/.test(navigator.userAgent)
|
||||
);
|
||||
|
||||
function insertTreeChildren(tree) {
|
||||
if (!enableLazy) {
|
||||
return;
|
||||
}
|
||||
var node = tree.node;
|
||||
var children = tree.children;
|
||||
if (children.length) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
insertTreeBefore(node, children[i], null);
|
||||
}
|
||||
} else if (tree.html != null) {
|
||||
node.innerHTML = tree.html;
|
||||
}
|
||||
}
|
||||
|
||||
function insertTreeBefore(parentNode, tree, referenceNode) {
|
||||
parentNode.insertBefore(tree.node, referenceNode);
|
||||
insertTreeChildren(tree);
|
||||
}
|
||||
|
||||
function replaceChildWithTree(oldNode, newTree) {
|
||||
oldNode.parentNode.replaceChild(newTree.node, oldNode);
|
||||
insertTreeChildren(newTree);
|
||||
}
|
||||
|
||||
function queueChild(parentTree, childTree) {
|
||||
if (enableLazy) {
|
||||
parentTree.children.push(childTree);
|
||||
} else {
|
||||
parentTree.node.appendChild(childTree.node);
|
||||
}
|
||||
}
|
||||
|
||||
function queueHTML(tree, html) {
|
||||
if (enableLazy) {
|
||||
tree.html = html;
|
||||
} else {
|
||||
tree.node.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
function DOMLazyTree(node) {
|
||||
return {
|
||||
node: node,
|
||||
children: [],
|
||||
html: null,
|
||||
};
|
||||
}
|
||||
|
||||
DOMLazyTree.insertTreeBefore = insertTreeBefore;
|
||||
DOMLazyTree.replaceChildWithTree = replaceChildWithTree;
|
||||
DOMLazyTree.queueChild = queueChild;
|
||||
DOMLazyTree.queueHTML = queueHTML;
|
||||
|
||||
module.exports = DOMLazyTree;
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var DOMLazyTree = require('DOMLazyTree');
|
||||
var ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
|
||||
var createNodesFromMarkup = require('createNodesFromMarkup');
|
||||
@@ -172,13 +173,12 @@ var Danger = {
|
||||
'server rendering. See ReactDOMServer.renderToString().'
|
||||
);
|
||||
|
||||
var newChild;
|
||||
if (typeof markup === 'string') {
|
||||
newChild = createNodesFromMarkup(markup, emptyFunction)[0];
|
||||
var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
|
||||
oldChild.parentNode.replaceChild(newChild, oldChild);
|
||||
} else {
|
||||
newChild = markup;
|
||||
DOMLazyTree.replaceChildWithTree(oldChild, markup);
|
||||
}
|
||||
oldChild.parentNode.replaceChild(newChild, oldChild);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
var AutoFocusUtils = require('AutoFocusUtils');
|
||||
var CSSPropertyOperations = require('CSSPropertyOperations');
|
||||
var DOMLazyTree = require('DOMLazyTree');
|
||||
var DOMNamespaces = require('DOMNamespaces');
|
||||
var DOMProperty = require('DOMProperty');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
@@ -41,7 +42,6 @@ var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
|
||||
var invariant = require('invariant');
|
||||
var isEventSupported = require('isEventSupported');
|
||||
var keyOf = require('keyOf');
|
||||
var setInnerHTML = require('setInnerHTML');
|
||||
var setTextContent = require('setTextContent');
|
||||
var shallowEqual = require('shallowEqual');
|
||||
var validateDOMNesting = require('validateDOMNesting');
|
||||
@@ -667,8 +667,9 @@ ReactDOMComponent.Mixin = {
|
||||
// Populate node cache
|
||||
ReactMount.getID(el);
|
||||
this._updateDOMProperties({}, props, transaction);
|
||||
this._createInitialChildren(transaction, props, context, el);
|
||||
mountImage = el;
|
||||
var lazyTree = DOMLazyTree(el);
|
||||
this._createInitialChildren(transaction, props, context, lazyTree);
|
||||
mountImage = lazyTree;
|
||||
} else {
|
||||
var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);
|
||||
var tagContent = this._createContentMarkup(transaction, props, context);
|
||||
@@ -814,12 +815,12 @@ ReactDOMComponent.Mixin = {
|
||||
}
|
||||
},
|
||||
|
||||
_createInitialChildren: function(transaction, props, context, el) {
|
||||
_createInitialChildren: function(transaction, props, context, lazyTree) {
|
||||
// Intentional use of != to avoid catching zero/false.
|
||||
var innerHTML = props.dangerouslySetInnerHTML;
|
||||
if (innerHTML != null) {
|
||||
if (innerHTML.__html != null) {
|
||||
setInnerHTML(el, innerHTML.__html);
|
||||
DOMLazyTree.queueHTML(lazyTree, innerHTML.__html);
|
||||
}
|
||||
} else {
|
||||
var contentToUse =
|
||||
@@ -827,7 +828,7 @@ ReactDOMComponent.Mixin = {
|
||||
var childrenToUse = contentToUse != null ? null : props.children;
|
||||
if (contentToUse != null) {
|
||||
// TODO: Validate that text is allowed as a child of this node
|
||||
setTextContent(el, contentToUse);
|
||||
setTextContent(lazyTree.node, contentToUse);
|
||||
} else if (childrenToUse != null) {
|
||||
var mountImages = this.mountChildren(
|
||||
childrenToUse,
|
||||
@@ -835,7 +836,7 @@ ReactDOMComponent.Mixin = {
|
||||
context
|
||||
);
|
||||
for (var i = 0; i < mountImages.length; i++) {
|
||||
el.appendChild(mountImages[i]);
|
||||
DOMLazyTree.queueChild(lazyTree, mountImages[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
'use strict';
|
||||
|
||||
var DOMChildrenOperations = require('DOMChildrenOperations');
|
||||
var DOMLazyTree = require('DOMLazyTree');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var ReactComponentBrowserEnvironment =
|
||||
require('ReactComponentBrowserEnvironment');
|
||||
@@ -106,7 +107,7 @@ assign(ReactDOMTextComponent.prototype, {
|
||||
// Populate node cache
|
||||
ReactMount.getID(el);
|
||||
setTextContent(el, this._stringText);
|
||||
return el;
|
||||
return DOMLazyTree(el);
|
||||
} else {
|
||||
var escapedText = escapeTextContentForBrowser(this._stringText);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user