Skip to content

Commit

Permalink
Perf improvements v2 (#278)
Browse files Browse the repository at this point in the history
* more perf improvements

* remove

* undo loop change

* put self closing in a set

* inline

* remove cross

* add missing void el

* create non-dimensional cahce

* last bits

* Create thick-islands-share.md
  • Loading branch information
JoviDeCroock committed Feb 25, 2023
1 parent fac1544 commit 8cf7cef
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 34 deletions.
10 changes: 10 additions & 0 deletions .changeset/thick-islands-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"preact-render-to-string": patch
---

Improve performance by

- storing the void_elements in a Set
- hoisting the `x-link` regex
- remove case-insensitive from regexes and calling `.toLowerCase()` instead
- caching suffixes for css-props
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 36 additions & 24 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ export default function renderToString(vnode, context) {
parent[CHILDREN] = [vnode];

try {
return _renderToString(vnode, context || {}, false, undefined, parent);
return _renderToString(
vnode,
context || EMPTY_OBJ,
false,
undefined,
parent
);
} finally {
// options._commit, we don't schedule any effects in this library right now,
// so we can pass an empty queue to this hook.
Expand All @@ -62,6 +68,8 @@ function markAsDirty() {
this.__d = true;
}

const EMPTY_OBJ = {};

/**
* @param {VNode} vnode
* @param {Record<string, unknown>} context
Expand All @@ -79,7 +87,7 @@ function renderClassComponent(vnode, context) {
// turn off stateful re-rendering:
c[DIRTY] = true;

if (c.state == null) c.state = {};
if (c.state == null) c.state = EMPTY_OBJ;

if (c[NEXT_STATE] == null) {
c[NEXT_STATE] = c.state;
Expand Down Expand Up @@ -163,8 +171,7 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
component;

// Invoke rendering on Components
let isComponent = typeof type === 'function';
if (isComponent) {
if (typeof type === 'function') {
if (type === Fragment) {
rendered = props.children;
} else {
Expand Down Expand Up @@ -307,15 +314,16 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
}
break;

default:
default: {
if (isSvgMode && XLINK.test(name)) {
name = name.toLowerCase().replace(/^xlink:?/, 'xlink:');
name = name.toLowerCase().replace(XLINK_REPLACE_REGEX, 'xlink:');
} else if (UNSAFE_NAME.test(name)) {
continue;
} else if (name[0] === 'a' && name[1] === 'r' && v != null) {
// serialize boolean aria-xyz attribute values as strings
v += '';
}
}
}

// write this attribute to the buffer
Expand Down Expand Up @@ -349,25 +357,29 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
if (options.unmount) options.unmount(vnode);

// Emit self-closing tag for empty void elements:
if (!html) {
switch (type) {
case 'area':
case 'base':
case 'br':
case 'col':
case 'embed':
case 'hr':
case 'img':
case 'input':
case 'link':
case 'meta':
case 'param':
case 'source':
case 'track':
case 'wbr':
return s + ' />';
}
if (!html && SELF_CLOSING.has(type)) {
return s + ' />';
}

return s + '>' + html + '</' + type + '>';
}

const XLINK_REPLACE_REGEX = /^xlink:?/;
const SELF_CLOSING = new Set([
'area',
'base',
'br',
'col',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr'
]);
24 changes: 14 additions & 10 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// DOM properties that should NOT have "px" added when numeric
export const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|^--/i;
export const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
export const UNSAFE_NAME = /[\s\n\\/='"\0<>]/;
export const XLINK = /^xlink:?./;

// DOM properties that should NOT have "px" added when numeric
const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|^--/;
const ENCODED_ENTITIES = /["&<]/;

/** @param {string} str */
Expand Down Expand Up @@ -50,6 +50,7 @@ export let isLargeString = (s, length, ignoreLines) =>
String(s).indexOf('<') !== -1;

const JS_TO_CSS = {};
const SUFFIX_CACHE = {};

const CSS_REGEX = /([A-Z])/g;
// Convert an Object style to a CSSText string
Expand All @@ -64,14 +65,17 @@ export function styleObjToCss(s) {
: JS_TO_CSS[prop] ||
(JS_TO_CSS[prop] = prop.replace(CSS_REGEX, '-$1').toLowerCase());

str =
str +
name +
':' +
val +
(typeof val === 'number' && IS_NON_DIMENSIONAL.test(prop) === false
? 'px;'
: ';');
let suffix = ';';
if (SUFFIX_CACHE[name]) {
suffix = 'px';
} else if (
typeof val === 'number' &&
IS_NON_DIMENSIONAL.test(prop.toLowerCase()) === false
) {
SUFFIX_CACHE[name] = true;
suffix = 'px';
}
str = str + name + ':' + val + suffix;
}
}
return str || undefined;
Expand Down

0 comments on commit 8cf7cef

Please sign in to comment.