[fix] low-level performance tuning

createDOMProps: avoid using default parameters as Babel compiles the
function to calls using 'arguments', which Chrome flags as a deopt.
Replace 'typeof' calls with slightly faster calls to constructor.

onLayout: batch layout measurements in a single requestAnimationFrame.

Close #490
This commit is contained in:
Tasveer Singh
2017-05-25 11:24:41 +01:00
committed by Nicolas Gallagher
parent d57fb6407a
commit a9c7b38df9
3 changed files with 42 additions and 18 deletions
+25 -3
View File
@@ -1,4 +1,5 @@
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
import setImmediate from 'fbjs/lib/setImmediate';
import setValueForStyles from '../../vendor/setValueForStyles';
const getRect = node => {
@@ -14,9 +15,14 @@ const getRect = node => {
return { height, left, top, width };
};
const measureLayout = (node, relativeToNativeNode, callback) => {
requestAnimationFrame(() => {
let hasRequestedAnimationFrame = false;
const measureLayoutQueue = [];
const processLayoutQueue = () => {
measureLayoutQueue.splice(0, 250).forEach((item) => {
const [node, relativeToNativeNode, callback] = item;
const relativeNode = relativeToNativeNode || (node && node.parentNode);
if (node && relativeNode) {
const relativeRect = getRect(relativeNode);
const { height, left, top, width } = getRect(node);
@@ -24,7 +30,23 @@ const measureLayout = (node, relativeToNativeNode, callback) => {
const y = top - relativeRect.top;
callback(x, y, width, height, left, top);
}
});
})
if (measureLayoutQueue.length > 0) {
setImmediate(processLayoutQueue);
}
}
const measureLayout = (node, relativeToNativeNode, callback) => {
if (!hasRequestedAnimationFrame) {
requestAnimationFrame(() => {
hasRequestedAnimationFrame = false;
processLayoutQueue();
});
}
hasRequestedAnimationFrame = true;
measureLayoutQueue.push([node, relativeToNativeNode, callback]);
};
const UIManager = {
+5 -7
View File
@@ -44,14 +44,12 @@ const createDOMElement = (component, props) => {
// normalize DOM events to match React Native events
// TODO: move this out of the render path
for (const prop in domProps) {
if (Object.prototype.hasOwnProperty.call(domProps, prop)) {
const isEventHandler = typeof prop === 'function' && eventHandlerNames[prop];
if (isEventHandler) {
domProps[prop] = wrapEventHandler(prop);
}
Object.keys(domProps).forEach((prop) => {
const isEventHandler = typeof prop === 'function' && eventHandlerNames[prop];
if (isEventHandler) {
domProps[prop] = wrapEventHandler(prop);
}
}
});
return <Component {...domProps} />;
};
+12 -8
View File
@@ -39,7 +39,11 @@ const pointerEventStyles = StyleSheet.create({
const resolver = style => StyleRegistry.resolve(style);
const createDOMProps = (rnProps, resolveStyle = resolver) => {
const createDOMProps = (rnProps, resolveStyle) => {
if (!resolveStyle) {
resolveStyle = resolver;
}
const props = rnProps || emptyObject;
const {
accessibilityLabel,
@@ -72,19 +76,19 @@ const createDOMProps = (rnProps, resolveStyle = resolver) => {
if (accessible === true) {
domProps.tabIndex = AccessibilityUtil.propsToTabIndex(props);
}
if (typeof accessibilityLabel === 'string') {
if (accessibilityLabel && accessibilityLabel.constructor === String) {
domProps['aria-label'] = accessibilityLabel;
}
if (typeof accessibilityLiveRegion === 'string') {
if (accessibilityLiveRegion && accessibilityLiveRegion.constructor === String) {
domProps['aria-live'] = accessibilityLiveRegion === 'none' ? 'off' : accessibilityLiveRegion;
}
if (typeof className === 'string' && className !== '') {
if (className && className.constructor === String) {
domProps.className = domProps.className ? `${domProps.className} ${className}` : className;
}
if (importantForAccessibility === 'no-hide-descendants') {
domProps['aria-hidden'] = true;
}
if (typeof role === 'string') {
if (role && role.constructor === String) {
domProps.role = role;
if (role === 'button') {
domProps.type = 'button';
@@ -92,13 +96,13 @@ const createDOMProps = (rnProps, resolveStyle = resolver) => {
domProps.rel = `${domProps.rel || ''} noopener noreferrer`;
}
}
if (style != null) {
if (style) {
domProps.style = style;
}
if (typeof testID === 'string') {
if (testID && testID.constructor === String) {
domProps['data-testid'] = testID;
}
if (typeof type === 'string') {
if (type && type.constructor === String) {
domProps.type = type;
}