From 4b01395457bf40735c11372e0c2969d9f43aef4e Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Sat, 15 Feb 2020 23:16:15 -0500 Subject: [PATCH 1/3] [experiment] Performance improvements for SSR --- src/ssr.js | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/ssr.js diff --git a/src/ssr.js b/src/ssr.js new file mode 100644 index 00000000..9cced9f3 --- /dev/null +++ b/src/ssr.js @@ -0,0 +1,165 @@ +import { encodeEntities, styleObjToCss, assign } from './util'; +import { options } from 'preact'; + +const VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/; + +const UNSAFE_NAME = /[\s\n\\/='"\0<>]/; + +/** + * Render Preact JSX + Components to an HTML string. + * @param {import('preact').VNode} vnode A Virtual DOM element to render. + */ +export default function (vnode) { + return renderVNode(vnode, {}, undefined); +} + +/** + * Render a Virtual DOM element (of any kind) to HTML. + * @param {import('preact').VNode|Array|string|number|boolean|null|undefined} vnode any renderable value + * @param {object} context context object, forks throughout the tree + * @param {any} selectValue the current select value, passed down through the tree to set + */ +function renderVNode(vnode, context, selectValue) { + if (vnode == null || vnode === false || vnode === true) { + return ''; + } + + if (typeof vnode !== 'object') { + return encodeEntities(vnode); + } + + if (Array.isArray(vnode)) { + let s = ''; + for (let i=0; i Date: Sat, 15 Feb 2020 23:17:11 -0500 Subject: [PATCH 2/3] Cache entity encoding --- src/util.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/util.js b/src/util.js index ac8a1b29..6c8a9aa0 100644 --- a/src/util.js +++ b/src/util.js @@ -1,11 +1,17 @@ // 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 let encodeEntities = s => String(s) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); +const ENC = {}; + +export function encodeEntities(s) { + s = String(s); + return ENC[s] || (ENC[s] = s + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + ); +} export let indent = (s, char) => String(s).replace(/(\n+)/g, '$1' + (char || '\t')); From 761ba18e17e1044fdc3370e993d6b9d7920a2ff8 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Sat, 15 Feb 2020 23:27:33 -0500 Subject: [PATCH 3/3] Update ssr.js --- src/ssr.js | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/ssr.js b/src/ssr.js index 9cced9f3..c58457ee 100644 --- a/src/ssr.js +++ b/src/ssr.js @@ -7,19 +7,11 @@ const UNSAFE_NAME = /[\s\n\\/='"\0<>]/; /** * Render Preact JSX + Components to an HTML string. - * @param {import('preact').VNode} vnode A Virtual DOM element to render. - */ -export default function (vnode) { - return renderVNode(vnode, {}, undefined); -} - -/** - * Render a Virtual DOM element (of any kind) to HTML. - * @param {import('preact').VNode|Array|string|number|boolean|null|undefined} vnode any renderable value + * @param {import('preact').VNode|string|number|null|boolean} vnode any renderable value * @param {object} context context object, forks throughout the tree * @param {any} selectValue the current select value, passed down through the tree to set */ -function renderVNode(vnode, context, selectValue) { +export default function renderToString(vnode, context, selectValue) { if (vnode == null || vnode === false || vnode === true) { return ''; } @@ -28,20 +20,19 @@ function renderVNode(vnode, context, selectValue) { return encodeEntities(vnode); } + context = context || {}; + if (Array.isArray(vnode)) { let s = ''; for (let i=0; i