Comparing version 1.5.2 to 1.5.3
# Changelog | ||
## 1.5.3- 2021-12-30 | ||
- Performance Improvements | ||
## 1.5.2- 2021-12-23 | ||
@@ -4,0 +8,0 @@ |
@@ -10,7 +10,5 @@ // Safari Polyfills | ||
// Safari Polyfills END | ||
const parser = (() => { | ||
const range = document.createRange(); | ||
range.selectNodeContents(range.createContextualFragment(`<${"template" /* template */}>`).lastChild); | ||
return range.createContextualFragment.bind(range); | ||
})(); | ||
const range = document.createRange(); | ||
range.selectNodeContents(range.createContextualFragment(`<${"template" /* template */}>`).lastChild); | ||
const parser = range.createContextualFragment.bind(range); | ||
const allNodeChanges = new WeakMap(); // Maps a Node against an array of changes. An array is necessary because a node can have multiple variables for one text / attribute. | ||
@@ -84,4 +82,3 @@ const elemEventFunctions = new WeakMap(); // Stores event functions in order to compare Elements against each other. | ||
function isDocumentFragment(node) { | ||
// getElementById exists in SVG too. I did not find a better way to identify a DocumentFragment | ||
return node.nodeName !== "svg" && "getElementById" in node; | ||
return node.nodeType === 11; | ||
} | ||
@@ -98,3 +95,9 @@ function isEventObject(obj) { | ||
function randomText() { | ||
return Math.random().toString(32).slice(2); | ||
const randomChars = "abcdefghijklmnopqrstuvwxyz0123456789"; | ||
let result = ""; | ||
for (var i = 0; i < 6; i++) { | ||
result += randomChars.charAt(Math.floor(Math.random() * randomChars.length)); | ||
} | ||
return result; | ||
// return Math.random().toString(32).slice(2); | ||
} | ||
@@ -198,6 +201,3 @@ function setGlobalSchedule(willSchedule) { | ||
// Delay Element iteration and manipulation after the elements have been added to the DOM. | ||
if (viewElements) { | ||
onRender(fillDOM, DOM.firstChild, DOM.firstChild, insertNodes, eventFunctions); | ||
} | ||
else { | ||
if (!viewElements) { | ||
fillDOM(DOM, insertNodes, eventFunctions); | ||
@@ -245,3 +245,2 @@ } | ||
return name({ ...props, children }); | ||
const flatChildren = children.map(getChildren).flat(); | ||
const elem = document.createElement(name); | ||
@@ -252,7 +251,4 @@ for (let i in props) { | ||
} | ||
elem.append(...flatChildren); | ||
if (viewElements) { | ||
onRender(setReactivity, elem, elem); | ||
} | ||
else { | ||
elem.append(...children); | ||
if (!viewElements) { | ||
setReactivity(elem); | ||
@@ -262,43 +258,19 @@ } | ||
} | ||
function getChildren(child) { | ||
return isObject(child) && !isNode(child) | ||
? Object.values(child) | ||
: child; | ||
} | ||
/* c8 ignore end */ | ||
function setReactivity(DOM, eventFunctions) { | ||
// Set events and reactive behaviour(checks for {{ key }} where key is on hydro) | ||
if (isTextNode(DOM) && !setReactivityElements.has(DOM)) { | ||
setReactivityElements.add(DOM); | ||
if (isTextNode(DOM)) { | ||
setReactivitySingle(DOM); | ||
return; | ||
} | ||
const root = [ | ||
...DOM.querySelectorAll("*"), | ||
].reverse(); | ||
if (!isDocumentFragment(DOM)) { | ||
root.push(DOM); | ||
} | ||
for (const elem of root) { | ||
if (setReactivityElements.has(elem)) { | ||
continue; | ||
} | ||
setReactivityElements.add(elem); | ||
// Check Text Nodes | ||
// This is somehow faster than createNodeIterator and createTreeWalker | ||
// https://esbench.com/bench/5e9c421c12464000a01e4359 | ||
let childNode = elem.firstChild; | ||
while (childNode) { | ||
if (isTextNode(childNode)) { | ||
setReactivitySingle(childNode); | ||
} | ||
childNode = childNode.nextSibling; | ||
} | ||
const elems = document.createNodeIterator(DOM, NodeFilter.SHOW_ELEMENT); | ||
let elem; | ||
while ((elem = elems.nextNode())) { | ||
for (const key of elem.getAttributeNames()) { | ||
// Set functions | ||
const val = elem.getAttribute(key); | ||
if (eventFunctions && key.startsWith("on")) { | ||
const eventName = key.replace(onEventRegex, ""); | ||
const event = eventFunctions[elem.getAttribute(key)]; | ||
const event = eventFunctions[val]; | ||
if (!event) { | ||
setReactivitySingle(elem, key); | ||
setReactivitySingle(elem, key, val); | ||
return; | ||
@@ -327,8 +299,15 @@ } | ||
else { | ||
setReactivitySingle(elem, key); | ||
setReactivitySingle(elem, key, val); | ||
} | ||
} | ||
let childNode = elem.firstChild; | ||
while (childNode) { | ||
if (isTextNode(childNode)) { | ||
setReactivitySingle(childNode); | ||
} | ||
childNode = childNode.nextSibling; | ||
} | ||
} | ||
} | ||
function setReactivitySingle(node, key) { | ||
function setReactivitySingle(node, key, val) { | ||
let attr_OR_text, match; | ||
@@ -339,3 +318,3 @@ if (isTextNode(node)) { | ||
else { | ||
attr_OR_text = node.getAttribute(key); | ||
attr_OR_text = val; | ||
if (attr_OR_text === "") { | ||
@@ -632,7 +611,3 @@ // e.g. checked attribute or two-way attribute | ||
const fn = lifecyleMap.get(node); | ||
if (viewElements) { | ||
schedule(fn); | ||
/* c8 ignore next 3 */ | ||
} | ||
else if (globalSchedule) { | ||
if (globalSchedule) { | ||
window.requestIdleCallback(fn); | ||
@@ -1364,2 +1339,3 @@ } | ||
runLifecyle(elem, onRenderMap); | ||
setReactivity(rootElem); | ||
viewElements = false; | ||
@@ -1397,2 +1373,3 @@ observe(data, (newData, oldData) => { | ||
} | ||
setReactivity(rootElem); | ||
viewElements = false; | ||
@@ -1399,0 +1376,0 @@ /* c8 ignore end */ |
{ | ||
"name": "hydro-js", | ||
"version": "1.5.2", | ||
"version": "1.5.3", | ||
"description": "A lightweight reactive library", | ||
@@ -5,0 +5,0 @@ "type": "module", |
@@ -87,9 +87,8 @@ declare global { | ||
const parser = (() => { | ||
const range = document.createRange(); | ||
range.selectNodeContents( | ||
range.createContextualFragment(`<${Placeholder.template}>`).lastChild! | ||
); | ||
return range.createContextualFragment.bind(range); | ||
})(); | ||
const range = document.createRange(); | ||
range.selectNodeContents( | ||
range.createContextualFragment(`<${Placeholder.template}>`).lastChild! | ||
); | ||
const parser = range.createContextualFragment.bind(range); | ||
const allNodeChanges = new WeakMap<Text | Element, nodeChanges>(); // Maps a Node against an array of changes. An array is necessary because a node can have multiple variables for one text / attribute. | ||
@@ -167,4 +166,3 @@ const elemEventFunctions = new WeakMap<Element, Array<EventListener>>(); // Stores event functions in order to compare Elements against each other. | ||
function isDocumentFragment(node: Node): node is DocumentFragment { | ||
// getElementById exists in SVG too. I did not find a better way to identify a DocumentFragment | ||
return node.nodeName !== "svg" && "getElementById" in node; | ||
return node.nodeType === 11; | ||
} | ||
@@ -183,3 +181,11 @@ function isEventObject(obj: object | unknown): obj is EventObject { | ||
function randomText() { | ||
return Math.random().toString(32).slice(2); | ||
const randomChars = "abcdefghijklmnopqrstuvwxyz0123456789"; | ||
let result = ""; | ||
for (var i = 0; i < 6; i++) { | ||
result += randomChars.charAt( | ||
Math.floor(Math.random() * randomChars.length) | ||
); | ||
} | ||
return result; | ||
// return Math.random().toString(32).slice(2); | ||
} | ||
@@ -304,11 +310,3 @@ | ||
// Delay Element iteration and manipulation after the elements have been added to the DOM. | ||
if (viewElements) { | ||
onRender( | ||
fillDOM, | ||
DOM.firstChild as Element, | ||
DOM.firstChild, | ||
insertNodes, | ||
eventFunctions | ||
); | ||
} else { | ||
if (!viewElements) { | ||
fillDOM(DOM, insertNodes, eventFunctions); | ||
@@ -367,3 +365,2 @@ } | ||
const flatChildren = children.map(getChildren).flat(); | ||
const elem = document.createElement(name); | ||
@@ -375,6 +372,4 @@ for (let i in props) { | ||
elem.append(...flatChildren); | ||
if (viewElements) { | ||
onRender(setReactivity, elem, elem); | ||
} else { | ||
elem.append(...children); | ||
if (!viewElements) { | ||
setReactivity(elem); | ||
@@ -384,7 +379,2 @@ } | ||
} | ||
function getChildren(child: any) { | ||
return isObject(child) && !isNode(child as Node) | ||
? Object.values(child) | ||
: child; | ||
} | ||
/* c8 ignore end */ | ||
@@ -395,42 +385,21 @@ function setReactivity( | ||
) { | ||
// Set events and reactive behaviour(checks for {{ key }} where key is on hydro) | ||
if (isTextNode(DOM) && !setReactivityElements.has(DOM)) { | ||
setReactivityElements.add(DOM); | ||
if (isTextNode(DOM)) { | ||
setReactivitySingle(DOM); | ||
return; | ||
} | ||
const root = [ | ||
...(DOM as DocumentFragment | Element).querySelectorAll("*"), | ||
].reverse(); | ||
if (!isDocumentFragment(DOM)) { | ||
root.push(DOM as Element); | ||
} | ||
for (const elem of root) { | ||
if (setReactivityElements.has(elem)) { | ||
continue; | ||
} | ||
setReactivityElements.add(elem); | ||
// Check Text Nodes | ||
// This is somehow faster than createNodeIterator and createTreeWalker | ||
// https://esbench.com/bench/5e9c421c12464000a01e4359 | ||
let childNode = elem.firstChild; | ||
while (childNode) { | ||
if (isTextNode(childNode)) { | ||
setReactivitySingle(childNode); | ||
} | ||
childNode = childNode.nextSibling; | ||
} | ||
const elems = document.createNodeIterator(DOM, NodeFilter.SHOW_ELEMENT); | ||
let elem; | ||
while ((elem = elems.nextNode() as Element)) { | ||
for (const key of elem.getAttributeNames()) { | ||
// Set functions | ||
const val = elem.getAttribute(key)!; | ||
if (eventFunctions && key.startsWith("on")) { | ||
const eventName = key!.replace(onEventRegex, ""); | ||
const event = eventFunctions[elem.getAttribute(key)!]; | ||
const eventName = key.replace(onEventRegex, ""); | ||
const event = eventFunctions[val]; | ||
if (!event) { | ||
setReactivitySingle(elem, key); | ||
setReactivitySingle(elem, key, val); | ||
return; | ||
} | ||
elem.removeAttribute(key); | ||
if (isEventObject(event)) { | ||
@@ -446,16 +415,28 @@ elem.addEventListener(eventName, event.event, event.options); | ||
if (elemEventFunctions.has(elem)) { | ||
elemEventFunctions.get(elem)!.push(event as EventListener); | ||
elemEventFunctions.get(elem)!.push(event); | ||
} else { | ||
elemEventFunctions.set(elem, [event as EventListener]); | ||
elemEventFunctions.set(elem, [event]); | ||
} | ||
} | ||
} else { | ||
setReactivitySingle(elem, key); | ||
setReactivitySingle(elem, key, val); | ||
} | ||
} | ||
let childNode = elem.firstChild; | ||
while (childNode) { | ||
if (isTextNode(childNode)) { | ||
setReactivitySingle(childNode); | ||
} | ||
childNode = childNode.nextSibling; | ||
} | ||
} | ||
} | ||
function setReactivitySingle(node: Text): void; // TS function overload | ||
function setReactivitySingle(node: Element, key: string): void; // TS function overload | ||
function setReactivitySingle(node: Element | Text, key?: string): void { | ||
function setReactivitySingle(node: Element, key: string, val: string): void; // TS function overload | ||
function setReactivitySingle( | ||
node: Element | Text, | ||
key?: string, | ||
val?: string | ||
): void { | ||
let attr_OR_text: string, match: RegExpMatchArray | null; | ||
@@ -466,3 +447,3 @@ | ||
} else { | ||
attr_OR_text = (node as Element).getAttribute(key!)!; | ||
attr_OR_text = val!; | ||
if (attr_OR_text! === "") { | ||
@@ -854,6 +835,3 @@ // e.g. checked attribute or two-way attribute | ||
if (viewElements) { | ||
schedule(fn); | ||
/* c8 ignore next 3 */ | ||
} else if (globalSchedule) { | ||
if (globalSchedule) { | ||
window.requestIdleCallback(fn as IdleRequestCallback); | ||
@@ -1689,2 +1667,4 @@ } else { | ||
for (const elem of elements) runLifecyle(elem as Element, onRenderMap); | ||
setReactivity(rootElem); | ||
viewElements = false; | ||
@@ -1726,2 +1706,3 @@ observe(data, (newData: typeof data, oldData: typeof data) => { | ||
} | ||
setReactivity(rootElem); | ||
viewElements = false; | ||
@@ -1728,0 +1709,0 @@ /* c8 ignore end */ |
278098
5077