Skip to content

Commit d1a9ddd

Browse files
Merge pull request #192 from preactjs/performance-improvements
2 parents e7fad70 + 95e4bc8 commit d1a9ddd

File tree

2 files changed

+49
-47
lines changed

2 files changed

+49
-47
lines changed

src/index.js

Lines changed: 44 additions & 37 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, Fragment } 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,50 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
5660
return '';
5761
}
5862

59-
// wrap array nodes in Fragment
60-
if (Array.isArray(vnode)) {
61-
vnode = createElement(Fragment, null, vnode);
63+
// #text nodes
64+
if (typeof vnode !== 'object') {
65+
return encodeEntities(vnode);
6266
}
6367

64-
let nodeName = vnode.type,
65-
props = vnode.props,
66-
isComponent = false;
67-
context = context || {};
68-
opts = opts || {};
69-
7068
let pretty = opts.pretty,
7169
indentChar = pretty && typeof pretty === 'string' ? pretty : '\t';
7270

73-
// #text nodes
74-
if (typeof vnode !== 'object' && !nodeName) {
75-
return encodeEntities(vnode);
71+
if (Array.isArray(vnode)) {
72+
let rendered = '';
73+
for (let i = 0; i < vnode.length; i++) {
74+
if (pretty && i > 0) rendered += '\n';
75+
rendered += _renderToString(
76+
vnode[i],
77+
context,
78+
opts,
79+
inner,
80+
isSvgMode,
81+
selectValue
82+
);
83+
}
84+
return rendered;
7685
}
7786

87+
let nodeName = vnode.type,
88+
props = vnode.props,
89+
isComponent = false;
90+
7891
// components
7992
if (typeof nodeName === 'function') {
8093
isComponent = true;
8194
if (opts.shallow && (inner || opts.renderRootComponent === false)) {
8295
nodeName = getComponentName(nodeName);
8396
} else if (nodeName === Fragment) {
84-
let rendered = '';
85-
let children = [];
97+
const children = [];
8698
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;
99+
return _renderToString(
100+
children,
101+
context,
102+
opts,
103+
opts.shallowHighOrder !== false,
104+
isSvgMode,
105+
selectValue
106+
);
101107
} else {
102108
let rendered;
103109

@@ -197,7 +203,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
197203
}
198204

199205
// render JSX to HTML
200-
let s = '',
206+
let s = '<' + nodeName,
201207
propChildren,
202208
html;
203209

@@ -215,7 +221,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
215221
continue;
216222
}
217223

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

220226
if (
221227
!(opts && opts.allAttributes) &&
@@ -287,18 +293,19 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
287293

288294
// account for >1 multiline attribute
289295
if (pretty) {
290-
let sub = s.replace(/^\n\s*/, ' ');
296+
let sub = s.replace(/\n\s*/, ' ');
291297
if (sub !== s && !~sub.indexOf('\n')) s = sub;
292298
else if (pretty && ~s.indexOf('\n')) s += '\n';
293299
}
294300

295-
s = `<${nodeName}${s}>`;
296-
if (UNSAFE_NAME.test(String(nodeName)))
301+
s += '>';
302+
303+
if (UNSAFE_NAME.test(nodeName))
297304
throw new Error(`${nodeName} is not a valid HTML tag name in ${s}`);
298305

299306
let isVoid =
300-
VOID_ELEMENTS.test(String(nodeName)) ||
301-
(opts.voidElements && opts.voidElements.test(String(nodeName)));
307+
VOID_ELEMENTS.test(nodeName) ||
308+
(opts.voidElements && opts.voidElements.test(nodeName));
302309
let pieces = [];
303310

304311
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)