Skip to content

Commit b6aa5a7

Browse files
committed
Performance improvements!
1 parent e7fad70 commit b6aa5a7

File tree

2 files changed

+40
-48
lines changed

2 files changed

+40
-48
lines changed

src/index.js

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
assign,
77
getChildren
88
} from './util';
9-
import { options, Fragment, createElement } from 'preact';
9+
import { options } from 'preact';
10+
11+
/** @typedef {import('preact').VNode} VNode */
1012

1113
const SHALLOW = { shallow: true };
1214

@@ -28,7 +30,7 @@ const noop = () => {};
2830
* @param {Boolean} [options.shallow=false] If `true`, renders nested Components as HTML elements (`<Foo a="b" />`).
2931
* @param {Boolean} [options.xml=false] If `true`, uses self-closing tags for elements without children.
3032
* @param {Boolean} [options.pretty=false] If `true`, adds whitespace for readability
31-
* @param {RegEx|undefined} [options.voidElements] RegeEx that matches elements that are considered void (self-closing)
33+
* @param {RegExp|undefined} [options.voidElements] RegeEx that matches elements that are considered void (self-closing)
3234
*/
3335
renderToString.render = renderToString;
3436

@@ -43,6 +45,8 @@ let shallowRender = (vnode, context) => renderToString(vnode, context, SHALLOW);
4345

4446
const EMPTY_ARR = [];
4547
function renderToString(vnode, context, opts) {
48+
context = context || {};
49+
opts = opts || {};
4650
const res = _renderToString(vnode, context, opts);
4751
// options._commit, we don't schedule any effects in this library right now,
4852
// so we can pass an empty queue to this hook.
@@ -56,48 +60,40 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
5660
return '';
5761
}
5862

63+
// #text nodes
64+
if (typeof vnode !== 'object') {
65+
return encodeEntities(vnode);
66+
}
67+
68+
let pretty = opts.pretty,
69+
indentChar = pretty && typeof pretty === 'string' ? pretty : '\t';
70+
5971
// wrap array nodes in Fragment
6072
if (Array.isArray(vnode)) {
61-
vnode = createElement(Fragment, null, vnode);
73+
let rendered = '';
74+
for (let i = 0; i < vnode.length; i++) {
75+
if (pretty && i > 0) rendered += '\n';
76+
rendered += _renderToString(
77+
vnode[i],
78+
context,
79+
opts,
80+
inner,
81+
isSvgMode,
82+
selectValue
83+
);
84+
}
85+
return rendered;
6286
}
6387

6488
let nodeName = vnode.type,
6589
props = vnode.props,
6690
isComponent = false;
67-
context = context || {};
68-
opts = opts || {};
69-
70-
let pretty = opts.pretty,
71-
indentChar = pretty && typeof pretty === 'string' ? pretty : '\t';
72-
73-
// #text nodes
74-
if (typeof vnode !== 'object' && !nodeName) {
75-
return encodeEntities(vnode);
76-
}
7791

7892
// components
7993
if (typeof nodeName === 'function') {
8094
isComponent = true;
8195
if (opts.shallow && (inner || opts.renderRootComponent === false)) {
8296
nodeName = getComponentName(nodeName);
83-
} else if (nodeName === Fragment) {
84-
let rendered = '';
85-
let children = [];
86-
getChildren(children, vnode.props.children);
87-
88-
for (let i = 0; i < children.length; i++) {
89-
rendered +=
90-
(i > 0 && pretty ? '\n' : '') +
91-
_renderToString(
92-
children[i],
93-
context,
94-
opts,
95-
opts.shallowHighOrder !== false,
96-
isSvgMode,
97-
selectValue
98-
);
99-
}
100-
return rendered;
10197
} else {
10298
let rendered;
10399

@@ -197,7 +193,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
197193
}
198194

199195
// render JSX to HTML
200-
let s = '',
196+
let s = '<' + nodeName,
201197
propChildren,
202198
html;
203199

@@ -215,7 +211,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
215211
continue;
216212
}
217213

218-
if (name.match(/[\s\n\\/='"\0<>]/)) continue;
214+
if (UNSAFE_NAME.test(name)) continue;
219215

220216
if (
221217
!(opts && opts.allAttributes) &&
@@ -287,18 +283,19 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
287283

288284
// account for >1 multiline attribute
289285
if (pretty) {
290-
let sub = s.replace(/^\n\s*/, ' ');
286+
let sub = s.replace(/\n\s*/, ' ');
291287
if (sub !== s && !~sub.indexOf('\n')) s = sub;
292288
else if (pretty && ~s.indexOf('\n')) s += '\n';
293289
}
294290

295-
s = `<${nodeName}${s}>`;
296-
if (UNSAFE_NAME.test(String(nodeName)))
291+
s += '>';
292+
293+
if (UNSAFE_NAME.test(nodeName))
297294
throw new Error(`${nodeName} is not a valid HTML tag name in ${s}`);
298295

299296
let isVoid =
300-
VOID_ELEMENTS.test(String(nodeName)) ||
301-
(opts.voidElements && opts.voidElements.test(String(nodeName)));
297+
VOID_ELEMENTS.test(nodeName) ||
298+
(opts.voidElements && opts.voidElements.test(nodeName));
302299
let pieces = [];
303300

304301
let children;

src/util.js

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
// DOM properties that should NOT have "px" added when numeric
22
export const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|^--/i;
33

4-
const HTML_ENTITY_REG = /[&<>"]/g;
5-
const tagsToReplace = {
6-
'&': '&amp;',
7-
'<': '&lt;',
8-
'>': '&gt;',
9-
'"': '&quot;'
10-
};
11-
const replaceTag = (tag) => tagsToReplace[tag] || tag;
124
export function encodeEntities(s) {
13-
if (typeof s !== 'string') s = String(s);
14-
return s.replace(HTML_ENTITY_REG, replaceTag);
5+
return String(s)
6+
.replace(/&/g, '&amp;')
7+
.replace(/</g, '&lt;')
8+
.replace(/>/g, '&gt;')
9+
.replace(/"/g, '&quot;');
1510
}
1611

1712
export let indent = (s, char) =>

0 commit comments

Comments
 (0)