1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2025-07-29 07:00:16 +02:00

v0.9.2 — 🐛 types, on.defer and other small (#36)

* 🔤  T now uses DocumentFragment

* 🔤

* 🔤 

* 🐛 lint

*  cleanup

*  🔤 lib download

*  🔤 ui

*  reorganize files

*  on.host

* 🐛 on.* types

*  🔤 cdn

* 🔤 converter

* 🐛 signal.set(value, force)

*  🔤

* 🔤  converter - convert also comments

*  bs/build

* 🔤 ui p14

* 🔤

* 🔤 Examples

* 🔤

* 🐛 now only el(..., string|number)

* 🐛 fixes #38

* 🔤

*  on.host → on.defer

* 🔤

* 📺
This commit is contained in:
2025-03-16 11:30:42 +01:00
committed by GitHub
parent 25d475ec04
commit f0dfdfde54
83 changed files with 4624 additions and 2919 deletions

View File

@@ -45,7 +45,7 @@ const references= {
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(...T`
el("p").append(T`
As your applications grow, performance becomes increasingly important. dd<el> provides several
techniques to optimize rendering performance, especially when dealing with large lists or frequently
updating components. This guide focuses on memoization and other optimization strategies.
@@ -63,7 +63,7 @@ export function page({ pkg, info }){
el(code, { src: fileURL("./components/examples/optimization/intro.js"), page_id }),
el(h3, t`Memoization with memo: Native vs dd<el>`),
el("p").append(...T`
el("p").append(T`
In standard JavaScript applications, optimizing list rendering often involves manual caching
or relying on complex virtual DOM diffing algorithms. dd<el>'s ${el("code", "memo")} function
provides a simpler, more direct approach:
@@ -106,13 +106,13 @@ export function page({ pkg, info }){
)
)
),
el("p").append(...T`
el("p").append(T`
The ${el("a", references.memo_docs).append(el("code", "memo"))} function in dd<el> allows you to
cache and reuse DOM elements instead of recreating them on every render, which can
significantly improve performance for components that render frequently or contain heavy computations.
`),
el("p").append(...T`
el("p").append(T`
The memo system is particularly useful for:
`),
el("ul").append(
@@ -122,7 +122,7 @@ export function page({ pkg, info }){
),
el(h3, t`Using memo with Signal Rendering`),
el("p").append(...T`
el("p").append(T`
The most common use case for memoization is within ${el("code", "S.el()")} when rendering lists with
${el("code", "map()")}:
`),
@@ -136,7 +136,7 @@ export function page({ pkg, info }){
))))
`, page_id }),
el("p").append(...T`
el("p").append(T`
The ${el("code", "memo")} function in this context:
`),
el("ol").append(
@@ -149,7 +149,7 @@ export function page({ pkg, info }){
el(example, { src: fileURL("./components/examples/optimization/memo.js"), page_id }),
el(h3, t`Creating Memoization Scopes`),
el("p").append(...T`
el("p").append(T`
The ${el("code", "memo()")} uses cache store defined via the ${el("code", "memo.scope")} function.
That is actually what the ${el("code", "S.el")} is doing under the hood:
`),
@@ -173,7 +173,7 @@ export function page({ pkg, info }){
);
`, page_id }),
el("p").append(...T`
el("p").append(T`
The scope function accepts options to customize its behavior:
`),
el(code, { content: `
@@ -189,16 +189,20 @@ export function page({ pkg, info }){
signal: controller.signal
});
`, page_id }),
el("p").append(T`
You can use custom memo scope as function in (e. g. ${el("code", "S.el(signal, renderList)")}) and as
(Abort) signal use ${el("code", "scope.signal")}.
`),
el("div", { className: "function-table" }).append(
el("dl").append(
el("dt", t`onlyLast Option`),
el("dd").append(...T`Only keeps the cache from the most recent function call,
el("dd").append(T`Only keeps the cache from the most recent function call,
which is useful when the entire collection is replaced. ${el("strong", "This is default behavior of ")
.append(el("code", "S.el"))}!`),
el("dt", t`signal Option`),
el("dd").append(...T`An ${el("a", references.mdn_abort).append(el("code", "AbortSignal"))}
el("dd").append(T`An ${el("a", references.mdn_abort).append(el("code", "AbortSignal"))}
that will clear the cache when aborted, helping with memory management`)
)
),
@@ -206,18 +210,16 @@ export function page({ pkg, info }){
el(h3, t`Additional Optimization Techniques`),
el("h4", t`Minimizing Signal Updates`),
el("p").append(...T`
el("p").append(T`
Signals are efficient, but unnecessary updates can impact performance:
`),
el("ul").append(
el("li", t`Avoid setting signal values that haven't actually changed`),
el("li", t`For frequently updating values (like scroll position), consider debouncing`),
el("li", t`Keep signal computations small and focused`),
el("li", t`Use derived signals to compute values only when dependencies change`)
),
el("h4", t`Optimizing List Rendering`),
el("p").append(...T`
el("p").append(T`
Beyond memoization, consider these approaches for optimizing list rendering:
`),
el("ul").append(
@@ -228,17 +230,17 @@ export function page({ pkg, info }){
),
el("div", { className: "tip" }).append(
el("p").append(...T`
el("p").append(T`
Memoization works best when your keys are stable and unique. Use IDs or other persistent
identifiers rather than array indices, which can change when items are reordered.
`),
el("p").append(...T`
el("p").append(T`
Alternatively you can use any “jsonable” value as key, when the primitive values arent enough.
`)
),
el("h4", t`Memory Management`),
el("p").append(...T`
el("p").append(T`
To prevent memory leaks and reduce memory consumption:
`),
el("ul").append(
@@ -249,43 +251,55 @@ export function page({ pkg, info }){
),
el("h4", t`Choosing the Right Optimization Approach`),
el("p").append(...T`
While memo is powerful, it's not always the best solution:
el("p").append(T`
While ${el("code", "memo")} is powerful, different scenarios call for different optimization techniques:
`),
el("table").append(
el("thead").append(
el("tr").append(
el("th", "Approach"),
el("th", "When to use")
)
),
el("tbody").append(
el("tr").append(
el("td", "memo"),
el("td", "Lists with stable items that infrequently change position")
),
el("tr").append(
el("td", "Signal computations"),
el("td", "Derived values that depend on other signals")
),
el("tr").append(
el("td", "Debouncing"),
el("td", "High-frequency events like scroll or resize")
),
el("tr").append(
el("td", "Stateful components"),
el("td", "Complex components with internal state")
)
el("div", { className: "function-table" }).append(
el("dl").append(
el("dt", t`memo`),
el("dd").append(T`
Best for list rendering where items rarely change or only their properties update.
${el("code", "todos.map(todo => memo(todo.id, () => el(TodoItem, todo)))")}
Use when you need to cache and reuse DOM elements to avoid recreating them on every render.
`),
el("dt", t`Signal computations`),
el("dd").append(T`
Ideal for derived values that depend on other signals and need to auto-update.
${el("code", "const totalPrice = S(() => items.get().reduce((t, i) => t + i.price, 0))")}
Use when calculated values need to stay in sync with changing source data.
`),
el("dt", t`Debouncing/Throttling`),
el("dd").append(T`
Essential for high-frequency events (scroll, resize) or rapidly changing input values.
${el("code", "debounce(e => searchQuery.set(e.target.value), 300)")}
Use to limit the rate at which expensive operations execute when triggered by fast events.
`),
el("dt", t`memo.scope`),
el("dd").append(T`
Useful for using memoization inside any function: ${el("code",
"const renderList = memo.scope(items => items.map(...))")}. Use to create isolated memoization
contexts that can be cleared or managed independently.
`),
el("dt", t`Stateful components`),
el("dd").append(T`
For complex UI components with internal state management.
${el("code", "el(ComplexComponent, { initialState, onChange })")}
Use when a component needs to encapsulate and manage its own state and lifecycle.
`)
)
),
el(h3, t`Known Issues and Limitations`),
el("p").append(...T`
el("p").append(T`
While memoization is a powerful optimization technique, there are some limitations and edge cases to be aware of:
`),
el("h4", t`Document Fragments and Memoization`),
el("p").append(...T`
el("p").append(T`
One important limitation to understand is how memoization interacts with
${el("a", references.mdn_fragment).append("DocumentFragment")} objects.
Functions like ${el("code", "S.el")} internally use DocumentFragment to efficiently handle multiple elements,
@@ -305,7 +319,7 @@ export function page({ pkg, info }){
container.append(memoizedFragment); // Nothing gets appended
`, page_id }),
el("p").append(...T`
el("p").append(T`
This happens because a DocumentFragment is emptied when it's appended to the DOM. When the fragment
is cached by memo and reused, it's already empty.
`),
@@ -327,7 +341,7 @@ export function page({ pkg, info }){
`, page_id })
),
el("p").append(...T`
el("p").append(T`
Generally, you should either:
`),
el("ol").append(
@@ -337,7 +351,7 @@ export function page({ pkg, info }){
),
el("div", { className: "note" }).append(
el("p").append(...T`
el("p").append(T`
This limitation isn't specific to dd<el> but is related to how DocumentFragment works in the DOM.
Once a fragment is appended to the DOM, its child nodes are moved from the fragment to the target element,
leaving the original fragment empty.
@@ -345,11 +359,11 @@ export function page({ pkg, info }){
),
el(h3, t`Performance Debugging`),
el("p").append(...T`
el("p").append(T`
To identify performance bottlenecks in your dd<el> applications:
`),
el("ol").append(
el("li").append(...T`Use ${el("a", references.mdn_perf).append("browser performance tools")} to profile
el("li").append(T`Use ${el("a", references.mdn_perf).append("browser performance tools")} to profile
rendering times`),
el("li", t`Check for excessive signal updates using S.on() listeners with console.log`),
el("li", t`Verify memo usage by inspecting cache hit rates`),
@@ -357,26 +371,26 @@ export function page({ pkg, info }){
),
el("div", { className: "note" }).append(
el("p").append(...T`
el("p").append(T`
For more details on debugging, see the ${el("a", { href: "p07-debugging.html", textContent: "Debugging" })} page.
`)
),
el(h3, t`Best Practices for Optimized Rendering`),
el("ol").append(
el("li").append(...T`
el("li").append(T`
${el("strong", "Use memo for list items:")} Memoize items in lists, especially when they contain complex components.
`),
el("li").append(...T`
el("li").append(T`
${el("strong", "Clean up with AbortSignals:")} Connect memo caches to component lifecycles using AbortSignals.
`),
el("li").append(...T`
el("li").append(T`
${el("strong", "Profile before optimizing:")} Identify actual bottlenecks before adding optimization.
`),
el("li").append(...T`
el("li").append(T`
${el("strong", "Use derived signals:")} Compute derived values efficiently with signal computations.
`),
el("li").append(...T`
el("li").append(T`
${el("strong", "Avoid memoizing fragments:")} Memoize individual elements or use container elements
instead of DocumentFragments.
`)