React inlining: Refactor to reduce parsing cost

- Have the `jsx` helper do the `defaultProps` work instead of calling `defaultProps` inline.
- Put `key` after `props` and make it optional.
- Inline `children` as rest args instead of in the object.
- Rename `createRawReactElement` to `jsx`. I wish I was kidding.

Most of these are silly microoptimizations. In my test file (based off an internal RN app), this reduces the parsing overhead of inlining from around 1% to 0.1% in JSC and from 0.6% to 0.0% in V8 (compared to element inlining before this commit).

Once parsed, the initial render with inlining is the same speed as not inlining in JSC and ~1% slower in V8. A second initial render in the same context (reusing the function objects, JIT, etc) is 2.0% faster in JSC and 5.5% faster in V8.
This commit is contained in:
Ben Alpert
2015-11-12 17:44:58 -08:00
parent 4b5a284efb
commit d1b8db1532
20 changed files with 72 additions and 74 deletions

View File

@@ -29,14 +29,12 @@ export default function ({ types: t }) {
if (hasRefOrSpread(open.attributes)) return;
// init
let isComponent = true;
let props = t.objectExpression([]);
let key = t.nullLiteral();
let key = null;
let type = open.name;
if (t.isJSXIdentifier(type) && t.react.isCompatTag(type.name)) {
type = t.stringLiteral(type.name);
isComponent = false;
}
function pushProp(objProps, key, value) {
@@ -54,24 +52,16 @@ export default function ({ types: t }) {
}
}
if (node.children.length) {
let args = [type, props];
if (key || node.children.length) {
let children = t.react.buildChildren(node);
if (children.length) {
children = children.length === 1 ? children[0] : t.arrayExpression(children);
pushProp(props.properties, t.identifier("children"), children);
}
args.push(
key || t.unaryExpression("void", t.numericLiteral(0), true),
...children
);
}
if (isComponent) {
let defProps = t.memberExpression(type, t.identifier("defaultProps"));
if (props.properties.length) {
props = t.callExpression(file.addHelper("defaultProps"), [defProps, props]);
} else {
props = t.logicalExpression("||", defProps, props);
}
}
let el = t.callExpression(file.addHelper("createRawReactElement"), [type, key, props]);
let el = t.callExpression(file.addHelper("jsx"), args);
path.replaceWith(el);
}
}

View File

@@ -1,4 +1,3 @@
babelHelpers.createRawReactElement("div", null, {
children: "foo",
children: "bar"
});
babelHelpers.jsx("div", {
children: "foo"
}, void 0, "bar");

View File

@@ -1,3 +1,3 @@
babelHelpers.createRawReactElement(Baz, null, babelHelpers.defaultProps(Baz.defaultProps, {
babelHelpers.jsx(Baz, {
foo: "bar"
}));
});

View File

@@ -1 +1 @@
babelHelpers.createRawReactElement(Baz, null, Baz.defaultProps || {});
babelHelpers.jsx(Baz, {});

View File

@@ -1,6 +1,6 @@
var TestComponent = React.createClass({
render: function () {
return babelHelpers.createRawReactElement("span", null, {
return babelHelpers.jsx("span", {
className: this.props.someProp
});
}

View File

@@ -1,3 +1,3 @@
babelHelpers.createRawReactElement("foo", null, {
babelHelpers.jsx("foo", {
bar: "foo"
});

View File

@@ -1 +1 @@
babelHelpers.createRawReactElement("foo", null, {});
babelHelpers.jsx("foo", {});

View File

@@ -1,3 +1,3 @@
babelHelpers.createRawReactElement(Foo, "foo" + "baz", babelHelpers.defaultProps(Foo.defaultProps, {
babelHelpers.jsx(Foo, {
"data-value": "bar"
}));
}, "foo" + "baz");

View File

@@ -1,3 +1,3 @@
babelHelpers.createRawReactElement(Foo, "foo", babelHelpers.defaultProps(Foo.defaultProps, {
babelHelpers.jsx(Foo, {
"data-value": "bar"
}));
}, "foo");

View File

@@ -1 +1 @@
babelHelpers.createRawReactElement(Baz, null, Baz.defaultProps || {});
babelHelpers.jsx(Baz, {}, void 0);

View File

@@ -1,4 +1,3 @@
babelHelpers.createRawReactElement(Foo, null, babelHelpers.defaultProps(Foo.defaultProps, {
className: "foo",
children: [bar, babelHelpers.createRawReactElement(Baz, "baz", Baz.defaultProps || {})]
}));
babelHelpers.jsx(Foo, {
className: "foo"
}, void 0, bar, babelHelpers.jsx(Baz, {}, "baz"));

View File

@@ -1,4 +1,3 @@
babelHelpers.createRawReactElement("div", null, {
className: "foo",
children: bar
});
babelHelpers.jsx("div", {
className: "foo"
}, void 0, bar);

View File

@@ -1,4 +1,3 @@
babelHelpers.createRawReactElement("div", null, {
className: "foo",
children: [bar, babelHelpers.createRawReactElement(Baz, "baz", Baz.defaultProps || {})]
});
babelHelpers.jsx("div", {
className: "foo"
}, void 0, bar, babelHelpers.jsx(Baz, {}, "baz"));

View File

@@ -1,3 +1,3 @@
babelHelpers.createRawReactElement(Baz, null, babelHelpers.defaultProps(Baz.defaultProps, {
babelHelpers.jsx(Baz, {
foo: "bar"
}));
});

View File

@@ -1 +1 @@
babelHelpers.createRawReactElement(Baz, null, Baz.defaultProps || {});
babelHelpers.jsx(Baz, {});

View File

@@ -1,3 +1,3 @@
babelHelpers.createRawReactElement("foo", null, {
babelHelpers.jsx("foo", {
bar: "foo"
});

View File

@@ -1 +1 @@
babelHelpers.createRawReactElement("foo", null, {});
babelHelpers.jsx("foo", {});

View File

@@ -1,3 +1,3 @@
babelHelpers.createRawReactElement(Foo, null, babelHelpers.defaultProps(Foo.defaultProps, {
babelHelpers.jsx(Foo, {
bar: true
}));
});