Ben Alpert d1b8db1532 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.
2015-11-12 18:05:57 -08:00

70 lines
2.0 KiB
JavaScript

export default function ({ types: t }) {
function hasRefOrSpread(attrs) {
for (let i = 0; i < attrs.length; i++) {
let attr = attrs[i];
if (t.isJSXSpreadAttribute(attr)) return true;
if (isJSXAttributeOfName(attr, "ref")) return true;
}
return false;
}
function isJSXAttributeOfName(attr, name) {
return t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: name });
}
function getAttributeValue(attr) {
let value = attr.value;
if (!value) return t.identifier("true");
if (t.isJSXExpressionContainer(value)) value = value.expression;
return value;
}
return {
visitor: {
JSXElement(path, file) {
let { node } = path;
// filter
let open = node.openingElement;
if (hasRefOrSpread(open.attributes)) return;
// init
let props = t.objectExpression([]);
let key = null;
let type = open.name;
if (t.isJSXIdentifier(type) && t.react.isCompatTag(type.name)) {
type = t.stringLiteral(type.name);
}
function pushProp(objProps, key, value) {
objProps.push(t.objectProperty(key, value));
}
// props
for (let attr of (open.attributes: Array<Object>)) {
if (isJSXAttributeOfName(attr, "key")) {
key = getAttributeValue(attr);
} else {
let name = attr.name.name;
let propertyKey = t.isValidIdentifier(name) ? t.identifier(name) : t.stringLiteral(name);
pushProp(props.properties, propertyKey, getAttributeValue(attr));
}
}
let args = [type, props];
if (key || node.children.length) {
let children = t.react.buildChildren(node);
args.push(
key || t.unaryExpression("void", t.numericLiteral(0), true),
...children
);
}
let el = t.callExpression(file.addHelper("jsx"), args);
path.replaceWith(el);
}
}
};
}