mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-04-11 15:15:33 +02:00
🔤 ⚡ T now uses DocumentFragment
This commit is contained in:
parent
25d475ec04
commit
0a2d17ac6f
@ -27,7 +27,7 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Welcome to Deka DOM Elements (dd<el> or DDE) — a lightweight library for building dynamic UIs with
|
Welcome to Deka DOM Elements (dd<el> or DDE) — a lightweight library for building dynamic UIs with
|
||||||
a declarative syntax that stays close to the native DOM API. dd<el> gives you powerful reactive tools
|
a declarative syntax that stays close to the native DOM API. dd<el> gives you powerful reactive tools
|
||||||
without the complexity and overhead of larger frameworks.
|
without the complexity and overhead of larger frameworks.
|
||||||
@ -45,7 +45,7 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/introducing/helloWorld.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/introducing/helloWorld.js"), page_id }),
|
||||||
|
|
||||||
el(h3, { textContent: t`The 3PS Pattern: A Better Way to Build UIs`, id: "h-3ps" }),
|
el(h3, { textContent: t`The 3PS Pattern: A Better Way to Build UIs`, id: "h-3ps" }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
At the heart of dd<el> is the 3PS (3-Part Separation) pattern. This simple yet powerful approach helps you
|
At the heart of dd<el> is the 3PS (3-Part Separation) pattern. This simple yet powerful approach helps you
|
||||||
organize your UI code into three distinct areas, making your applications more maintainable and easier
|
organize your UI code into three distinct areas, making your applications more maintainable and easier
|
||||||
to reason about.
|
to reason about.
|
||||||
@ -62,22 +62,22 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The 3PS pattern separates your code into three clear parts:
|
The 3PS pattern separates your code into three clear parts:
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Create State")}: Define your application’s reactive data using signals
|
${el("strong", "Create State")}: Define your application’s reactive data using signals
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Bind to Elements")}: Define how UI elements react to state changes
|
${el("strong", "Bind to Elements")}: Define how UI elements react to state changes
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Update State")}: Modify state in response to user events or other triggers
|
${el("strong", "Update State")}: Modify state in response to user events or other triggers
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
By separating these concerns, your code becomes more modular, testable, and easier to maintain. This
|
By separating these concerns, your code becomes more modular, testable, and easier to maintain. This
|
||||||
approach shares principles with more formal patterns like ${el("a", { textContent: "MVVM",
|
approach shares principles with more formal patterns like ${el("a", { textContent: "MVVM",
|
||||||
...references.w_mvv })} and ${el("a", { textContent: "MVC", ...references.w_mvc })}, but with less
|
...references.w_mvv })} and ${el("a", { textContent: "MVC", ...references.w_mvc })}, but with less
|
||||||
@ -85,7 +85,7 @@ export function page({ pkg, info }){
|
|||||||
`),
|
`),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The 3PS pattern becomes especially powerful when combined with components, allowing you to create
|
The 3PS pattern becomes especially powerful when combined with components, allowing you to create
|
||||||
reusable pieces of UI with encapsulated state and behavior. You’ll learn more about this in the
|
reusable pieces of UI with encapsulated state and behavior. You’ll learn more about this in the
|
||||||
following sections.
|
following sections.
|
||||||
@ -93,36 +93,36 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`How to Use This Documentation`),
|
el(h3, t`How to Use This Documentation`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This guide will take you through dd<el>’s features step by step:
|
This guide will take you through dd<el>’s features step by step:
|
||||||
`),
|
`),
|
||||||
el("ol", { start: 2 }).append(
|
el("ol", { start: 2 }).append(
|
||||||
el("li").append(...T`${el("a", { href: "p02-elements.html" }).append(el("strong", "Elements"))} — Creating
|
el("li").append(T`${el("a", { href: "p02-elements.html" }).append(el("strong", "Elements"))} — Creating
|
||||||
and manipulating DOM elements`),
|
and manipulating DOM elements`),
|
||||||
el("li").append(...T`${el("a", { href: "p03-events.html" }).append(el("strong", "Events and Addons"))} —
|
el("li").append(T`${el("a", { href: "p03-events.html" }).append(el("strong", "Events and Addons"))} —
|
||||||
Handling user interactions and lifecycle events`),
|
Handling user interactions and lifecycle events`),
|
||||||
el("li").append(...T`${el("a", { href: "p04-signals.html" }).append(el("strong", "Signals"))} — Adding
|
el("li").append(T`${el("a", { href: "p04-signals.html" }).append(el("strong", "Signals"))} — Adding
|
||||||
reactivity to your UI`),
|
reactivity to your UI`),
|
||||||
el("li").append(...T`${el("a", { href: "p05-scopes.html" }).append(el("strong", "Scopes"))} — Managing
|
el("li").append(T`${el("a", { href: "p05-scopes.html" }).append(el("strong", "Scopes"))} — Managing
|
||||||
component lifecycles`),
|
component lifecycles`),
|
||||||
el("li").append(...T`${el("a", { href: "p06-customElement.html" }).append(el("strong", "Web Components"))} —
|
el("li").append(T`${el("a", { href: "p06-customElement.html" }).append(el("strong", "Web Components"))} —
|
||||||
Building native custom elements`),
|
Building native custom elements`),
|
||||||
el("li").append(...T`${el("a", { href: "p07-debugging.html" }).append(el("strong", "Debugging"))} — Tools to
|
el("li").append(T`${el("a", { href: "p07-debugging.html" }).append(el("strong", "Debugging"))} — Tools to
|
||||||
help you build and fix your apps`),
|
help you build and fix your apps`),
|
||||||
el("li").append(...T`${el("a", { href: "p08-extensions.html" }).append(el("strong", "Extensions"))} —
|
el("li").append(T`${el("a", { href: "p08-extensions.html" }).append(el("strong", "Extensions"))} —
|
||||||
Integrating third-party functionalities`),
|
Integrating third-party functionalities`),
|
||||||
el("li").append(...T`${el("a", { href: "p09-optimization.html" })
|
el("li").append(T`${el("a", { href: "p09-optimization.html" })
|
||||||
.append(el("strong", "Performance Optimization"))} — Techniques for optimizing your applications`),
|
.append(el("strong", "Performance Optimization"))} — Techniques for optimizing your applications`),
|
||||||
el("li").append(...T`${el("a", { href: "p10-todomvc.html" }).append(el("strong", "TodoMVC"))} — A real-world
|
el("li").append(T`${el("a", { href: "p10-todomvc.html" }).append(el("strong", "TodoMVC"))} — A real-world
|
||||||
application implementation`),
|
application implementation`),
|
||||||
el("li").append(...T`${el("a", { href: "p11-ssr.html" }).append(el("strong", "SSR"))} — Server-side
|
el("li").append(T`${el("a", { href: "p11-ssr.html" }).append(el("strong", "SSR"))} — Server-side
|
||||||
rendering with dd<el>`),
|
rendering with dd<el>`),
|
||||||
el("li").append(...T`${el("a", { href: "p12-ireland.html" }).append(el("strong", "Ireland Components"))} —
|
el("li").append(T`${el("a", { href: "p12-ireland.html" }).append(el("strong", "Ireland Components"))} —
|
||||||
Interactive demos with server-side pre-rendering`),
|
Interactive demos with server-side pre-rendering`),
|
||||||
el("li").append(...T`${el("a", { href: "p13-appendix.html" }).append(el("strong", "Appendix & Summary"))} —
|
el("li").append(T`${el("a", { href: "p13-appendix.html" }).append(el("strong", "Appendix & Summary"))} —
|
||||||
Comprehensive reference and best practices`),
|
Comprehensive reference and best practices`),
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Each section builds on the previous ones, so we recommend following them in order.
|
Each section builds on the previous ones, so we recommend following them in order.
|
||||||
Let’s get started with the basics of creating elements!
|
Let’s get started with the basics of creating elements!
|
||||||
`),
|
`),
|
||||||
|
@ -49,7 +49,7 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Building user interfaces in JavaScript often involves creating and manipulating DOM elements.
|
Building user interfaces in JavaScript often involves creating and manipulating DOM elements.
|
||||||
dd<el> provides a simple yet powerful approach to element creation that is declarative, chainable,
|
dd<el> provides a simple yet powerful approach to element creation that is declarative, chainable,
|
||||||
and maintains a clean syntax close to HTML structure.
|
and maintains a clean syntax close to HTML structure.
|
||||||
@ -68,7 +68,7 @@ export function page({ pkg, info }){
|
|||||||
el(code, { src: fileURL("./components/examples/elements/intro.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/elements/intro.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Creating Elements: Native vs dd<el>`),
|
el(h3, t`Creating Elements: Native vs dd<el>`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
In standard JavaScript, you create DOM elements using the
|
In standard JavaScript, you create DOM elements using the
|
||||||
${el("a", references.mdn_create).append(el("code", "document.createElement()"))} method
|
${el("a", references.mdn_create).append(el("code", "document.createElement()"))} method
|
||||||
and then set properties individually or with ${el("code", "Object.assign()")}:
|
and then set properties individually or with ${el("code", "Object.assign()")}:
|
||||||
@ -85,14 +85,14 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("code", "el")} function provides a simple wrapper around ${el("code", "document.createElement")}
|
The ${el("code", "el")} function provides a simple wrapper around ${el("code", "document.createElement")}
|
||||||
with enhanced property assignment.
|
with enhanced property assignment.
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Advanced Property Assignment`),
|
el(h3, t`Advanced Property Assignment`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("code", "assign")} function is the heart of dd<el>’s element property handling. It is internally
|
The ${el("code", "assign")} function is the heart of dd<el>’s element property handling. It is internally
|
||||||
used to assign properties using the ${el("code", "el")} function. ${el("code", "assign")} provides
|
used to assign properties using the ${el("code", "el")} function. ${el("code", "assign")} provides
|
||||||
intelligent assignment of both ${el("a", { textContent: "properties (IDL)", ...references.mdn_idl })}
|
intelligent assignment of both ${el("a", { textContent: "properties (IDL)", ...references.mdn_idl })}
|
||||||
@ -104,28 +104,28 @@ export function page({ pkg, info }){
|
|||||||
el("dd", t`Prefers IDL properties, falls back to setAttribute() when no writable property exists`),
|
el("dd", t`Prefers IDL properties, falls back to setAttribute() when no writable property exists`),
|
||||||
|
|
||||||
el("dt", t`Data and ARIA Attributes`),
|
el("dt", t`Data and ARIA Attributes`),
|
||||||
el("dd").append(...T`Both ${el("code", "dataset")}.* and ${el("code", "data-")}* syntaxes supported
|
el("dd").append(T`Both ${el("code", "dataset")}.* and ${el("code", "data-")}* syntaxes supported
|
||||||
(same for ${el("em", "ARIA")})`),
|
(same for ${el("em", "ARIA")})`),
|
||||||
|
|
||||||
el("dt", t`Style Handling`),
|
el("dt", t`Style Handling`),
|
||||||
el("dd").append(...T`Accepts string or object notation for ${el("code", "style")} property`),
|
el("dd").append(T`Accepts string or object notation for ${el("code", "style")} property`),
|
||||||
|
|
||||||
el("dt", t`Class Management`),
|
el("dt", t`Class Management`),
|
||||||
el("dd").append(...T`Works with ${el("code", "className")}, ${el("code", "class")}, or ${el("code",
|
el("dd").append(T`Works with ${el("code", "className")}, ${el("code", "class")}, or ${el("code",
|
||||||
"classList")} object for toggling classes`),
|
"classList")} object for toggling classes`),
|
||||||
|
|
||||||
el("dt", t`Force Modes`),
|
el("dt", t`Force Modes`),
|
||||||
el("dd").append(...T`Use ${el("code", "=")} prefix to force attribute mode, ${el("code", ".")} prefix to
|
el("dd").append(T`Use ${el("code", "=")} prefix to force attribute mode, ${el("code", ".")} prefix to
|
||||||
force property mode`),
|
force property mode`),
|
||||||
|
|
||||||
el("dt", t`Attribute Removal`),
|
el("dt", t`Attribute Removal`),
|
||||||
el("dd").append(...T`Pass ${el("code", "undefined")} to remove a property or attribute`)
|
el("dd").append(T`Pass ${el("code", "undefined")} to remove a property or attribute`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el(example, { src: fileURL("./components/examples/elements/dekaAssign.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/elements/dekaAssign.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
You can explore standard HTML element properties in the MDN documentation for
|
You can explore standard HTML element properties in the MDN documentation for
|
||||||
${el("a", { textContent: "HTMLElement", ...references.mdn_el })} (base class)
|
${el("a", { textContent: "HTMLElement", ...references.mdn_el })} (base class)
|
||||||
and specific element interfaces like ${el("a", { textContent: "HTMLParagraphElement", ...references.mdn_p })}.
|
and specific element interfaces like ${el("a", { textContent: "HTMLParagraphElement", ...references.mdn_p })}.
|
||||||
@ -133,7 +133,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Building DOM Trees with Chainable Methods`),
|
el(h3, t`Building DOM Trees with Chainable Methods`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
One of the most powerful features of dd<el> is its approach to building element trees.
|
One of the most powerful features of dd<el> is its approach to building element trees.
|
||||||
Unlike the native DOM API which doesn’t return the parent after ${el("code", "append()")}, dd<el>’s
|
Unlike the native DOM API which doesn’t return the parent after ${el("code", "append()")}, dd<el>’s
|
||||||
${el("code", "append()")} always returns the parent element:
|
${el("code", "append()")} always returns the parent element:
|
||||||
@ -150,28 +150,28 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This chainable pattern is much cleaner and easier to follow, especially for deeply nested elements.
|
This chainable pattern is much cleaner and easier to follow, especially for deeply nested elements.
|
||||||
It also makes it simple to add multiple children to a parent element in a single fluent expression.
|
It also makes it simple to add multiple children to a parent element in a single fluent expression.
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Using Components to Build UI Fragments`),
|
el(h3, t`Using Components to Build UI Fragments`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("code", "el")} function is overloaded to support both tag names and function components.
|
The ${el("code", "el")} function is overloaded to support both tag names and function components.
|
||||||
This lets you refactor complex UI trees into reusable pieces:
|
This lets you refactor complex UI trees into reusable pieces:
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js"), page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Component functions receive the properties object as their first argument, just like regular elements.
|
Component functions receive the properties object as their first argument, just like regular elements.
|
||||||
This makes it easy to pass data down to components and create reusable UI fragments.
|
This makes it easy to pass data down to components and create reusable UI fragments.
|
||||||
`),
|
`),
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
It’s helpful to use naming conventions similar to native DOM elements for your components.
|
It’s helpful to use naming conventions similar to native DOM elements for your components.
|
||||||
This allows you to keeps your code consistent with the DOM API.
|
This allows you to keeps your code consistent with the DOM API.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Use ${el("a", { textContent: "destructuring assignment", ...references.mdn_destruct })}
|
Use ${el("a", { textContent: "destructuring assignment", ...references.mdn_destruct })}
|
||||||
to extract the properties from the ${el("code", "props")} and pass them to the component element:
|
to extract the properties from the ${el("code", "props")} and pass them to the component element:
|
||||||
${el("code", "function component({ className }){ return el(\"p\", { className }); }")} for make
|
${el("code", "function component({ className }){ return el(\"p\", { className }); }")} for make
|
||||||
@ -180,29 +180,29 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Working with SVG and Other Namespaces`),
|
el(h3, t`Working with SVG and Other Namespaces`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
For non-HTML elements like SVG, MathML, or custom namespaces, dd<el> provides the ${el("code", "elNS")}
|
For non-HTML elements like SVG, MathML, or custom namespaces, dd<el> provides the ${el("code", "elNS")}
|
||||||
function which corresponds to the native ${el("a", references.mdn_ns).append(el("code",
|
function which corresponds to the native ${el("a", references.mdn_ns).append(el("code",
|
||||||
"document.createElementNS"))}:
|
"document.createElementNS"))}:
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This function returns a namespace-specific element creator, allowing you to work with any element type
|
This function returns a namespace-specific element creator, allowing you to work with any element type
|
||||||
using the same consistent interface.
|
using the same consistent interface.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Best Practices for Declarative DOM Creation`),
|
el(h3, t`Best Practices for Declarative DOM Creation`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Use component functions for reusable UI fragments:")} Extract common UI patterns
|
${el("strong", "Use component functions for reusable UI fragments:")} Extract common UI patterns
|
||||||
into reusable functions that return elements.
|
into reusable functions that return elements.
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Leverage destructuring for cleaner component code:")} Use
|
${el("strong", "Leverage destructuring for cleaner component code:")} Use
|
||||||
${el("a", { textContent: "destructuring", ...references.mdn_destruct })} to extract properties
|
${el("a", { textContent: "destructuring", ...references.mdn_destruct })} to extract properties
|
||||||
from the props object for cleaner component code.
|
from the props object for cleaner component code.
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Leverage chainable methods for better performance:")} Use chainable methods
|
${el("strong", "Leverage chainable methods for better performance:")} Use chainable methods
|
||||||
${el("code", ".append()")} to build complex DOM trees for better performance and cleaner code.
|
${el("code", ".append()")} to build complex DOM trees for better performance and cleaner code.
|
||||||
`),
|
`),
|
||||||
|
@ -41,7 +41,7 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Events are at the core of interactive web applications. dd<el> provides a clean, declarative approach to
|
Events are at the core of interactive web applications. dd<el> provides a clean, declarative approach to
|
||||||
handling DOM events and extends this pattern with a powerful Addon system to incorporate additional
|
handling DOM events and extends this pattern with a powerful Addon system to incorporate additional
|
||||||
functionalities into your UI templates.
|
functionalities into your UI templates.
|
||||||
@ -60,7 +60,7 @@ export function page({ pkg, info }){
|
|||||||
el(code, { src: fileURL("./components/examples/events/intro.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/events/intro.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Events and Listeners: Two Approaches`),
|
el(h3, t`Events and Listeners: Two Approaches`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
In JavaScript you can listen to native DOM events using
|
In JavaScript you can listen to native DOM events using
|
||||||
${el("a", references.mdn_listen).append(el("code", "element.addEventListener(type, listener, options)"))}.
|
${el("a", references.mdn_listen).append(el("code", "element.addEventListener(type, listener, options)"))}.
|
||||||
dd<el> provides an alternative approach with arguments ordered differently to better fit its declarative
|
dd<el> provides an alternative approach with arguments ordered differently to better fit its declarative
|
||||||
@ -78,7 +78,7 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The main benefit of dd<el>’s approach is that it works as an Addon (see below), making it easy to integrate
|
The main benefit of dd<el>’s approach is that it works as an Addon (see below), making it easy to integrate
|
||||||
directly into element declarations.
|
directly into element declarations.
|
||||||
`),
|
`),
|
||||||
@ -86,13 +86,13 @@ export function page({ pkg, info }){
|
|||||||
|
|
||||||
el(h3, t`Removing Event Listeners`),
|
el(h3, t`Removing Event Listeners`),
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Unlike the native addEventListener/removeEventListener pattern, dd<el> uses the ${el("a", {
|
Unlike the native addEventListener/removeEventListener pattern, dd<el> uses the ${el("a", {
|
||||||
textContent: "AbortSignal", ...references.mdn_abortListener })} for declarative approach for removal:
|
textContent: "AbortSignal", ...references.mdn_abortListener })} for declarative approach for removal:
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This is the same for signals (see next section) and works well with scopes and library extendability (
|
This is the same for signals (see next section) and works well with scopes and library extendability (
|
||||||
see scopes and extensions section).
|
see scopes and extensions section).
|
||||||
`),
|
`),
|
||||||
@ -102,7 +102,7 @@ export function page({ pkg, info }){
|
|||||||
el("div", { className: "tab", dataTab: "html-attr" }).append(
|
el("div", { className: "tab", dataTab: "html-attr" }).append(
|
||||||
el("h4", t`HTML Attribute Style`),
|
el("h4", t`HTML Attribute Style`),
|
||||||
el(code, { src: fileURL("./components/examples/events/attribute-event.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/events/attribute-event.js"), page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Forces usage as an HTML attribute. Corresponds to
|
Forces usage as an HTML attribute. Corresponds to
|
||||||
${el("code", `<button onclick="console.log(event)">click me</button>`)}. This can be particularly
|
${el("code", `<button onclick="console.log(event)">click me</button>`)}. This can be particularly
|
||||||
useful for SSR scenarios.
|
useful for SSR scenarios.
|
||||||
@ -119,13 +119,13 @@ export function page({ pkg, info }){
|
|||||||
el("p", t`Uses the addon pattern (so adds the event listener to the element), see above.`)
|
el("p", t`Uses the addon pattern (so adds the event listener to the element), see above.`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
For a deeper comparison of these approaches, see
|
For a deeper comparison of these approaches, see
|
||||||
${el("a", { textContent: "WebReflection’s detailed analysis", ...references.web_events })}.
|
${el("a", { textContent: "WebReflection’s detailed analysis", ...references.web_events })}.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Understanding Addons`),
|
el(h3, t`Understanding Addons`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Addons are a powerful pattern in dd<el> that extends beyond just event handling.
|
Addons are a powerful pattern in dd<el> that extends beyond just event handling.
|
||||||
An Addon is any function that accepts an HTML element as its first parameter.
|
An Addon is any function that accepts an HTML element as its first parameter.
|
||||||
`),
|
`),
|
||||||
@ -139,22 +139,22 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Capture element references`)
|
el("li", t`Capture element references`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
You can use Addons as ≥3rd argument of the ${el("code", "el")} function, making it possible to
|
You can use Addons as ≥3rd argument of the ${el("code", "el")} function, making it possible to
|
||||||
extend your templates with additional functionality:
|
extend your templates with additional functionality:
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
As the example shows, you can provide types in JSDoc+TypeScript using the global type
|
As the example shows, you can provide types in JSDoc+TypeScript using the global type
|
||||||
${el("code", "ddeElementAddon")}. Notice how Addons can also be used to get element references.
|
${el("code", "ddeElementAddon")}. Notice how Addons can also be used to get element references.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Lifecycle Events`),
|
el(h3, t`Lifecycle Events`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Addons are called immediately when an element is created, even before it’s connected to the live DOM.
|
Addons are called immediately when an element is created, even before it’s connected to the live DOM.
|
||||||
You can think of an Addon as an "oncreate" event handler.
|
You can think of an Addon as an "oncreate" event handler.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
dd<el> provides two additional lifecycle events that correspond to ${el("a", { textContent:
|
dd<el> provides two additional lifecycle events that correspond to ${el("a", { textContent:
|
||||||
"custom element", ...references.mdn_customElements })} lifecycle callbacks:
|
"custom element", ...references.mdn_customElements })} lifecycle callbacks:
|
||||||
`),
|
`),
|
||||||
@ -170,7 +170,7 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
For regular elements (non-custom elements), dd<el> uses ${el("a",
|
For regular elements (non-custom elements), dd<el> uses ${el("a",
|
||||||
references.mdn_mutation).append(el("code", "MutationObserver"), " | MDN")} internally to track
|
references.mdn_mutation).append(el("code", "MutationObserver"), " | MDN")} internally to track
|
||||||
lifecycle events.
|
lifecycle events.
|
||||||
@ -179,28 +179,28 @@ export function page({ pkg, info }){
|
|||||||
|
|
||||||
el("div", { className: "warning" }).append(
|
el("div", { className: "warning" }).append(
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Always use ${el("code", "on.*")} functions as library must ensure proper (MutationObserver)
|
Always use ${el("code", "on.*")} functions as library must ensure proper (MutationObserver)
|
||||||
registration, not ${el("code", "on('dde:*', ...)")}, even the native event system is used with event
|
registration, not ${el("code", "on('dde:*', ...)")}, even the native event system is used with event
|
||||||
names prefixed with ${el("code", "dde:")}.
|
names prefixed with ${el("code", "dde:")}.
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Use lifecycle events sparingly, as they require internal tracking
|
Use lifecycle events sparingly, as they require internal tracking
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Leverage parent-child relationships: when a parent is removed, all children are also removed
|
Leverage parent-child relationships: when a parent is removed, all children are also removed
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
…see section later in documentation regarding hosts elements
|
…see section later in documentation regarding hosts elements
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
dd<el> ensures that connected/disconnected events fire only once for better predictability
|
dd<el> ensures that connected/disconnected events fire only once for better predictability
|
||||||
`)
|
`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Dispatching Custom Events`),
|
el(h3, t`Dispatching Custom Events`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This makes it easy to implement component communication through events, following standard web platform
|
This makes it easy to implement component communication through events, following standard web platform
|
||||||
patterns. The curried approach allows for easy reuse of event dispatchers throughout your application.
|
patterns. The curried approach allows for easy reuse of event dispatchers throughout your application.
|
||||||
`),
|
`),
|
||||||
@ -209,17 +209,17 @@ export function page({ pkg, info }){
|
|||||||
|
|
||||||
el(h3, t`Best Practices`),
|
el(h3, t`Best Practices`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Clean up listeners")}: Use AbortSignal to prevent memory leaks
|
${el("strong", "Clean up listeners")}: Use AbortSignal to prevent memory leaks
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Leverage lifecycle events")}: For component setup and teardown
|
${el("strong", "Leverage lifecycle events")}: For component setup and teardown
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Delegate when possible")}: Add listeners to container elements when handling many
|
${el("strong", "Delegate when possible")}: Add listeners to container elements when handling many
|
||||||
similar elements
|
similar elements
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Maintain consistency")}: Choose one event binding approach and stick with it
|
${el("strong", "Maintain consistency")}: Choose one event binding approach and stick with it
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
@ -44,7 +44,7 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Signals provide a simple yet powerful way to create reactive applications with dd<el>. They handle the
|
Signals provide a simple yet powerful way to create reactive applications with dd<el>. They handle the
|
||||||
fundamental challenge of keeping your UI in sync with changing data in a declarative, efficient way.
|
fundamental challenge of keeping your UI in sync with changing data in a declarative, efficient way.
|
||||||
`),
|
`),
|
||||||
@ -55,13 +55,13 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Automatic UI updates when data changes`),
|
el("li", t`Automatic UI updates when data changes`),
|
||||||
el("li", t`Clean separation between data, logic, and UI`),
|
el("li", t`Clean separation between data, logic, and UI`),
|
||||||
el("li", t`Small runtime with minimal overhead`),
|
el("li", t`Small runtime with minimal overhead`),
|
||||||
el("li").append(...T`${el("strong", "In future")} no dependencies or framework lock-in`)
|
el("li").append(T`${el("strong", "In future")} no dependencies or framework lock-in`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el(code, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`The 3-Part Structure of Signals`),
|
el(h3, t`The 3-Part Structure of Signals`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Signals organize your code into three distinct parts, following the
|
Signals organize your code into three distinct parts, following the
|
||||||
${el("a", { textContent: t`3PS principle`, href: "./#h-3ps" })}:
|
${el("a", { textContent: t`3PS principle`, href: "./#h-3ps" })}:
|
||||||
`),
|
`),
|
||||||
@ -85,7 +85,7 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/signals/signals.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/signals/signals.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Signals implement the ${el("a", { textContent: t`Publish–subscribe pattern`, ...references.wiki_pubsub
|
Signals implement the ${el("a", { textContent: t`Publish–subscribe pattern`, ...references.wiki_pubsub
|
||||||
})}, a form of ${el("a", { textContent: t`Event-driven programming`, ...references.wiki_event_driven
|
})}, a form of ${el("a", { textContent: t`Event-driven programming`, ...references.wiki_event_driven
|
||||||
})}. This architecture allows different parts of your application to stay synchronized through
|
})}. This architecture allows different parts of your application to stay synchronized through
|
||||||
@ -110,30 +110,30 @@ export function page({ pkg, info }){
|
|||||||
el("dd", t`S.on(signal, callback) → runs callback whenever signal changes`),
|
el("dd", t`S.on(signal, callback) → runs callback whenever signal changes`),
|
||||||
|
|
||||||
el("dt", t`Unsubscribing`),
|
el("dt", t`Unsubscribing`),
|
||||||
el("dd").append(...T`S.on(signal, callback, { signal: abortController.signal }) → Similarly to the
|
el("dd").append(T`S.on(signal, callback, { signal: abortController.signal }) → Similarly to the
|
||||||
${el("code", "on")} function to register DOM events listener.`)
|
${el("code", "on")} function to register DOM events listener.`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Signals can be created with any type of value, but they work best with ${el("a", { textContent:
|
Signals can be created with any type of value, but they work best with ${el("a", { textContent:
|
||||||
t`primitive types`, ...references.mdn_primitive })} like strings, numbers, and booleans. For complex
|
t`primitive types`, ...references.mdn_primitive })} like strings, numbers, and booleans. For complex
|
||||||
data types like objects and arrays, you’ll want to use Actions (covered below).
|
data types like objects and arrays, you’ll want to use Actions (covered below).
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Derived Signals: Computed Values`),
|
el(h3, t`Derived Signals: Computed Values`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Computed values (also called derived signals) automatically update when their dependencies change.
|
Computed values (also called derived signals) automatically update when their dependencies change.
|
||||||
Create them by passing ${el("strong", "a function")} to ${el("code", "S()")}:
|
Create them by passing ${el("strong", "a function")} to ${el("code", "S()")}:
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/signals/derived.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/signals/derived.js"), page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Derived signals are read-only - you can’t call ${el("code", ".set()")} on them. Their value is always
|
Derived signals are read-only - you can’t call ${el("code", ".set()")} on them. Their value is always
|
||||||
computed from their dependencies. They’re perfect for transforming or combining data from other signals.
|
computed from their dependencies. They’re perfect for transforming or combining data from other signals.
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Signal Actions: For Complex State`),
|
el(h3, t`Signal Actions: For Complex State`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When working with objects, arrays, or other complex data structures. Signal Actions provide
|
When working with objects, arrays, or other complex data structures. Signal Actions provide
|
||||||
a structured way to modify state while maintaining reactivity.
|
a structured way to modify state while maintaining reactivity.
|
||||||
`),
|
`),
|
||||||
@ -164,7 +164,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }))
|
`, page_id }))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
In some way, you can compare it with ${el("a", { textContent: "useReducer", ...references.mdn_use_reducer })}
|
In some way, you can compare it with ${el("a", { textContent: "useReducer", ...references.mdn_use_reducer })}
|
||||||
hook from React. So, the ${el("code", "S(<data>, <actions>)")} pattern creates a store “machine”. We can
|
hook from React. So, the ${el("code", "S(<data>, <actions>)")} pattern creates a store “machine”. We can
|
||||||
then invoke (dispatch) registered action by calling ${el("code", "S.action(<signal>, <name>, ...<args>)")}
|
then invoke (dispatch) registered action by calling ${el("code", "S.action(<signal>, <name>, ...<args>)")}
|
||||||
@ -174,7 +174,7 @@ export function page({ pkg, info }){
|
|||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/signals/actions-demo.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/signals/actions-demo.js"), page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Actions provide these benefits:
|
Actions provide these benefits:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -183,17 +183,17 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Prevent accidental direct mutations`),
|
el("li", t`Prevent accidental direct mutations`),
|
||||||
el("li", t`Act similar to reducers in other state management libraries`)
|
el("li", t`Act similar to reducers in other state management libraries`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Here’s a more complete example of a todo list using signal actions:
|
Here’s a more complete example of a todo list using signal actions:
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/signals/actions-todos.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/signals/actions-todos.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
${el("strong", "Special Action Methods")}: Signal actions can implement special lifecycle hooks:
|
${el("strong", "Special Action Methods")}: Signal actions can implement special lifecycle hooks:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("code", "[S.symbols.onclear]()")} - Called when the signal is cleared. Use it to clean up
|
${el("code", "[S.symbols.onclear]()")} - Called when the signal is cleared. Use it to clean up
|
||||||
resources.
|
resources.
|
||||||
`),
|
`),
|
||||||
@ -201,7 +201,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Connecting Signals to the DOM`),
|
el(h3, t`Connecting Signals to the DOM`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Signals really shine when connected to your UI. dd<el> provides several ways to bind signals to DOM elements:
|
Signals really shine when connected to your UI. dd<el> provides several ways to bind signals to DOM elements:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
@ -245,37 +245,37 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("code", "assign")} and ${el("code", "el")} functions detect signals automatically and handle binding.
|
The ${el("code", "assign")} and ${el("code", "el")} functions detect signals automatically and handle binding.
|
||||||
You can use special properties like ${el("code", "dataset")}, ${el("code", "ariaset")}, and
|
You can use special properties like ${el("code", "dataset")}, ${el("code", "ariaset")}, and
|
||||||
${el("code", "classList")} for fine-grained control over specific attribute types.
|
${el("code", "classList")} for fine-grained control over specific attribute types.
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
${el("code", "S.el()")} is especially powerful for conditional rendering and lists:
|
${el("code", "S.el()")} is especially powerful for conditional rendering and lists:
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/signals/dom-el.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/signals/dom-el.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Best Practices for Signals`),
|
el(h3, t`Best Practices for Signals`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Follow these guidelines to get the most out of signals:
|
Follow these guidelines to get the most out of signals:
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Keep signals small and focused")}: Use many small signals rather than a few large ones
|
${el("strong", "Keep signals small and focused")}: Use many small signals rather than a few large ones
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Use derived signals for computations")}: Don’t recompute values in multiple places
|
${el("strong", "Use derived signals for computations")}: Don’t recompute values in multiple places
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Clean up signal subscriptions")}: Use AbortController (scope.host()) to prevent memory
|
${el("strong", "Clean up signal subscriptions")}: Use AbortController (scope.host()) to prevent memory
|
||||||
leaks
|
leaks
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Use actions for complex state")}: Don’t directly mutate objects or arrays in signals
|
${el("strong", "Use actions for complex state")}: Don’t directly mutate objects or arrays in signals
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Avoid infinite loops")}: Be careful when one signal updates another in a subscription
|
${el("strong", "Avoid infinite loops")}: Be careful when one signal updates another in a subscription
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
@ -29,21 +29,21 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
For state-less components we can use functions as UI components (see “Elements” page). But in real life,
|
For state-less components we can use functions as UI components (see “Elements” page). But in real life,
|
||||||
we may need to handle the component’s life-cycle and provide JavaScript the way to properly use
|
we may need to handle the component’s life-cycle and provide JavaScript the way to properly use
|
||||||
the ${el("a", { textContent: t`Garbage collection`, ...references.garbage_collection })}.
|
the ${el("a", { textContent: t`Garbage collection`, ...references.garbage_collection })}.
|
||||||
`),
|
`),
|
||||||
el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }),
|
||||||
el("p").append(...T`The library therefore uses ${el("em", t`scopes`)} to provide these functionalities.`),
|
el("p").append(T`The library therefore uses ${el("em", t`scopes`)} to provide these functionalities.`),
|
||||||
|
|
||||||
el(h3, t`Understanding Host Elements and Scopes`),
|
el(h3, t`Understanding Host Elements and Scopes`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("strong", "host")} is the name for the element representing the component. This is typically the
|
The ${el("strong", "host")} is the name for the element representing the component. This is typically the
|
||||||
element returned by a function. To get a reference, you can use ${el("code", "scope.host()")}. To apply addons,
|
element returned by a function. To get a reference, you can use ${el("code", "scope.host()")}. To apply addons,
|
||||||
just use ${el("code", "scope.host(...<addons>)")}.
|
just use ${el("code", "scope.host(...<addons>)")}.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Scopes are primarily needed when signals are used in DOM templates (with ${el("code", "el")}, ${el("code",
|
Scopes are primarily needed when signals are used in DOM templates (with ${el("code", "el")}, ${el("code",
|
||||||
"assign")}, or ${el("code", "S.el")}). They provide a way for automatically removing signal listeners
|
"assign")}, or ${el("code", "S.el")}). They provide a way for automatically removing signal listeners
|
||||||
and cleaning up unused signals when components are removed from the DOM.
|
and cleaning up unused signals when components are removed from the DOM.
|
||||||
@ -86,19 +86,19 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
${el("strong", "Best Practice:")} Always capture the host reference (or other scope related values) at
|
${el("strong", "Best Practice:")} Always capture the host reference (or other scope related values) at
|
||||||
the beginning of your component function using ${el("code", "const { host } = scope")} to avoid
|
the beginning of your component function using ${el("code", "const { host } = scope")} to avoid
|
||||||
scope-related issues, especially with ${el("em", "asynchronous code")}.
|
scope-related issues, especially with ${el("em", "asynchronous code")}.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
If you are interested in the implementation details, see Class-Based Components section.
|
If you are interested in the implementation details, see Class-Based Components section.
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
el(code, { src: fileURL("./components/examples/scopes/good-practise.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/scopes/good-practise.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Class-Based Components`),
|
el(h3, t`Class-Based Components`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
While functional components are the primary pattern in dd<el>, you can also create class-based components.
|
While functional components are the primary pattern in dd<el>, you can also create class-based components.
|
||||||
For this, we implement function ${el("code", "elClass")} and use it to demonstrate implementation details
|
For this, we implement function ${el("code", "elClass")} and use it to demonstrate implementation details
|
||||||
for better understanding of the scope logic.
|
for better understanding of the scope logic.
|
||||||
@ -106,7 +106,7 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Automatic Cleanup with Scopes`),
|
el(h3, t`Automatic Cleanup with Scopes`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
One of the most powerful features of scopes is automatic cleanup when components are removed from the DOM.
|
One of the most powerful features of scopes is automatic cleanup when components are removed from the DOM.
|
||||||
This prevents memory leaks and ensures resources are properly released.
|
This prevents memory leaks and ensures resources are properly released.
|
||||||
`),
|
`),
|
||||||
@ -126,7 +126,7 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
In this example, when you click "Remove", the component is removed from the DOM, and all its associated
|
In this example, when you click "Remove", the component is removed from the DOM, and all its associated
|
||||||
resources are automatically cleaned up, including ${el("em",
|
resources are automatically cleaned up, including ${el("em",
|
||||||
"the signal subscription that updates the text content")}. This happens because the library
|
"the signal subscription that updates the text content")}. This happens because the library
|
||||||
@ -135,12 +135,12 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Declarative vs Imperative Components`),
|
el(h3, t`Declarative vs Imperative Components`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The library DOM API and signals work best when used declaratively. It means you split your app’s logic
|
The library DOM API and signals work best when used declaratively. It means you split your app’s logic
|
||||||
into three parts as introduced in ${el("a", { textContent: "Signals (3PS)", ...references.signals })}.
|
into three parts as introduced in ${el("a", { textContent: "Signals (3PS)", ...references.signals })}.
|
||||||
`),
|
`),
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Strictly speaking, the imperative way of using the library is not prohibited. Just be careful to avoid
|
Strictly speaking, the imperative way of using the library is not prohibited. Just be careful to avoid
|
||||||
mixing the declarative approach (using signals) with imperative manipulation of elements.
|
mixing the declarative approach (using signals) with imperative manipulation of elements.
|
||||||
`)
|
`)
|
||||||
@ -165,20 +165,20 @@ export function page({ pkg, info }){
|
|||||||
|
|
||||||
el(h3, t`Best Practices for Scopes and Components`),
|
el(h3, t`Best Practices for Scopes and Components`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Capture host early:")} Use ${el("code", "const { host } = scope")} at component start
|
${el("strong", "Capture host early:")} Use ${el("code", "const { host } = scope")} at component start
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Define signals as constants:")} ${el("code", "const counter = S(0);")}
|
${el("strong", "Define signals as constants:")} ${el("code", "const counter = S(0);")}
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Prefer declarative patterns:")} Use signals to drive UI updates rather than manual DOM
|
${el("strong", "Prefer declarative patterns:")} Use signals to drive UI updates rather than manual DOM
|
||||||
manipulation
|
manipulation
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Keep components focused:")} Each component should do one thing well
|
${el("strong", "Keep components focused:")} Each component should do one thing well
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Add explicit cleanup:")} For resources not managed by dd<el>, use ${el("code",
|
${el("strong", "Add explicit cleanup:")} For resources not managed by dd<el>, use ${el("code",
|
||||||
"on.disconnected")}
|
"on.disconnected")}
|
||||||
`)
|
`)
|
||||||
|
@ -59,7 +59,7 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
dd<el> pairs powerfully with ${el("a", references.mdn_web_components).append(el("strong", t`Web
|
dd<el> pairs powerfully with ${el("a", references.mdn_web_components).append(el("strong", t`Web
|
||||||
Components`))} to create reusable, encapsulated custom elements with all the benefits of dd<el>’s
|
Components`))} to create reusable, encapsulated custom elements with all the benefits of dd<el>’s
|
||||||
declarative DOM construction and reactivity system.
|
declarative DOM construction and reactivity system.
|
||||||
@ -76,29 +76,29 @@ export function page({ pkg, info }){
|
|||||||
el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Getting Started: Web Components Basics`),
|
el(h3, t`Getting Started: Web Components Basics`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Web Components are a set of standard browser APIs that let you create custom HTML elements with
|
Web Components are a set of standard browser APIs that let you create custom HTML elements with
|
||||||
encapsulated functionality. They consist of three main technologies:
|
encapsulated functionality. They consist of three main technologies:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Custom Elements:")} Create your own HTML tags with JS-defined behavior
|
${el("strong", "Custom Elements:")} Create your own HTML tags with JS-defined behavior
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Shadow DOM:")} Encapsulate styles and markup within a component
|
${el("strong", "Shadow DOM:")} Encapsulate styles and markup within a component
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "HTML Templates:")} Define reusable markup structures (${el("em",
|
${el("strong", "HTML Templates:")} Define reusable markup structures (${el("em",
|
||||||
"the dd<el> replaces this part")})
|
"the dd<el> replaces this part")})
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Let’s start with a basic Custom Element example without dd<el> to establish the foundation:
|
Let’s start with a basic Custom Element example without dd<el> to establish the foundation:
|
||||||
`),
|
`),
|
||||||
el(code, { src: fileURL("./components/examples/customElement/native-basic.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/customElement/native-basic.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
For complete information on Web Components, see the
|
For complete information on Web Components, see the
|
||||||
${el("a", references.mdn_custom_elements).append(el("strong", t`MDN documentation`))}.
|
${el("a", references.mdn_custom_elements).append(el("strong", t`MDN documentation`))}.
|
||||||
Also, ${el("a", references.custom_elements_tips).append(el("strong", t`Handy Custom Elements Patterns`))}
|
Also, ${el("a", references.custom_elements_tips).append(el("strong", t`Handy Custom Elements Patterns`))}
|
||||||
@ -107,10 +107,10 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`dd<el> Integration: Step 1 - Event Handling`),
|
el(h3, t`dd<el> Integration: Step 1 - Event Handling`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The first step in integrating dd<el> with Web Components is enabling dd<el>’s event system to work with your
|
The first step in integrating dd<el> with Web Components is enabling dd<el>’s event system to work with your
|
||||||
Custom Elements. This is done with ${el("code", "customElementWithDDE")}, which makes your Custom Element
|
Custom Elements. This is done with ${el("code", "customElementWithDDE")}, which makes your Custom Element
|
||||||
compatible with dd<el>’s event handling. (${el("em").append(...T`Notice that customElementWithDDE is
|
compatible with dd<el>’s event handling. (${el("em").append(T`Notice that customElementWithDDE is
|
||||||
actually`)} ${el("a", { textContent: "decorator", ...references.decorators })})
|
actually`)} ${el("a", { textContent: "decorator", ...references.decorators })})
|
||||||
`),
|
`),
|
||||||
el("div", { className: "function-table" }).append(
|
el("div", { className: "function-table" }).append(
|
||||||
@ -127,14 +127,14 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
${el("strong", "Key Point:")} The ${el("code", "customElementWithDDE")} function adds event dispatching
|
${el("strong", "Key Point:")} The ${el("code", "customElementWithDDE")} function adds event dispatching
|
||||||
to your Custom Element lifecycle methods, making them work seamlessly with dd<el>’s event system.
|
to your Custom Element lifecycle methods, making them work seamlessly with dd<el>’s event system.
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`dd<el> Integration: Step 2 - Rendering Components`),
|
el(h3, t`dd<el> Integration: Step 2 - Rendering Components`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The next step is to use dd<el>’s component rendering within your Custom Element. This is done with
|
The next step is to use dd<el>’s component rendering within your Custom Element. This is done with
|
||||||
${el("code", "customElementRender")}, which connects your dd<el> component function to the Custom Element.
|
${el("code", "customElementRender")}, which connects your dd<el> component function to the Custom Element.
|
||||||
`),
|
`),
|
||||||
@ -159,32 +159,32 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/customElement/dde.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/customElement/dde.js"), page_id }),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
In this example, we’re using Shadow DOM (${el("code", "this.attachShadow()")}) for encapsulation,
|
In this example, we’re using Shadow DOM (${el("code", "this.attachShadow()")}) for encapsulation,
|
||||||
but you can also render directly to the element with ${el("code", "customElementRender(this, ...)")}.
|
but you can also render directly to the element with ${el("code", "customElementRender(this, ...)")}.
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Reactive Web Components with Signals`),
|
el(h3, t`Reactive Web Components with Signals`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
One of the most powerful features of integrating dd<el> with Web Components is connecting HTML attributes
|
One of the most powerful features of integrating dd<el> with Web Components is connecting HTML attributes
|
||||||
to dd<el>’s reactive signals system. This creates truly reactive custom elements.
|
to dd<el>’s reactive signals system. This creates truly reactive custom elements.
|
||||||
`),
|
`),
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
${el("strong", "Two Ways to Handle Attributes:")}
|
${el("strong", "Two Ways to Handle Attributes:")}
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Using standard attribute access (${el("code", "this.getAttribute(<name>)")}) - Passes attributes as
|
Using standard attribute access (${el("code", "this.getAttribute(<name>)")}) - Passes attributes as
|
||||||
regular values (static)
|
regular values (static)
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("code", "S.observedAttributes")} - Transforms attributes into signals (reactive)
|
${el("code", "S.observedAttributes")} - Transforms attributes into signals (reactive)
|
||||||
`)
|
`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Using the ${el("code", "S.observedAttributes")} creates a reactive connection between your element’s
|
Using the ${el("code", "S.observedAttributes")} creates a reactive connection between your element’s
|
||||||
attributes and its internal rendering. When attributes change, your component automatically updates!
|
attributes and its internal rendering. When attributes change, your component automatically updates!
|
||||||
`),
|
`),
|
||||||
@ -203,7 +203,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Working with Shadow DOM`),
|
el(h3, t`Working with Shadow DOM`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Shadow DOM provides encapsulation for your component’s styles and markup. When using dd<el> with Shadow DOM,
|
Shadow DOM provides encapsulation for your component’s styles and markup. When using dd<el> with Shadow DOM,
|
||||||
you get the best of both worlds: encapsulation plus declarative DOM creation.
|
you get the best of both worlds: encapsulation plus declarative DOM creation.
|
||||||
`),
|
`),
|
||||||
@ -223,14 +223,14 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js"), page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
For more information on Shadow DOM, see
|
For more information on Shadow DOM, see
|
||||||
${el("a", { textContent: t`Using Shadow DOM`, ...references.mdn_shadow_dom_depth })}, or the comprehensive
|
${el("a", { textContent: t`Using Shadow DOM`, ...references.mdn_shadow_dom_depth })}, or the comprehensive
|
||||||
${el("a", { textContent: t`Shadow DOM in Depth`, ...references.shadow_dom_depth })}.
|
${el("a", { textContent: t`Shadow DOM in Depth`, ...references.shadow_dom_depth })}.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Working with Slots`),
|
el(h3, t`Working with Slots`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Besides the encapsulation, the Shadow DOM allows for using the ${el("a", references.mdn_shadow_dom_slot).append(
|
Besides the encapsulation, the Shadow DOM allows for using the ${el("a", references.mdn_shadow_dom_slot).append(
|
||||||
el("strong", t`<slot>`), t` element(s)`)}. You can simulate this feature using ${el("code", "simulateSlots")}:
|
el("strong", t`<slot>`), t` element(s)`)}. You can simulate this feature using ${el("code", "simulateSlots")}:
|
||||||
`),
|
`),
|
||||||
@ -246,23 +246,23 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Best Practices for Web Components with dd<el>`),
|
el(h3, t`Best Practices for Web Components with dd<el>`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When combining dd<el> with Web Components, follow these recommendations:
|
When combining dd<el> with Web Components, follow these recommendations:
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Always use customElementWithDDE")} to enable event integration
|
${el("strong", "Always use customElementWithDDE")} to enable event integration
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Prefer S.observedAttributes")} for reactive attribute connections
|
${el("strong", "Prefer S.observedAttributes")} for reactive attribute connections
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Create reusable component functions")} that your custom elements render
|
${el("strong", "Create reusable component functions")} that your custom elements render
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Use scope.host()")} to clean up event listeners and subscriptions
|
${el("strong", "Use scope.host()")} to clean up event listeners and subscriptions
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Add setters and getters")} for better property access to your element
|
${el("strong", "Add setters and getters")} for better property access to your element
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
@ -17,19 +17,19 @@ const fileURL= url=> new URL(url, import.meta.url);
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Debugging is an essential part of application development. This guide provides techniques
|
Debugging is an essential part of application development. This guide provides techniques
|
||||||
and best practices for debugging applications built with dd<el>, with a focus on signals.
|
and best practices for debugging applications built with dd<el>, with a focus on signals.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Debugging signals`),
|
el(h3, t`Debugging signals`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Signals are reactive primitives that update the UI when their values change. When debugging signals,
|
Signals are reactive primitives that update the UI when their values change. When debugging signals,
|
||||||
you need to track their values, understand their dependencies, and identify why updates are or aren't happening.
|
you need to track their values, understand their dependencies, and identify why updates are or aren't happening.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("h4", t`Inspecting signal values`),
|
el("h4", t`Inspecting signal values`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The simplest way to debug a signal is to log its current value by calling the get method:
|
The simplest way to debug a signal is to log its current value by calling the get method:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -38,7 +38,7 @@ export function page({ pkg, info }){
|
|||||||
// without triggering updates
|
// without triggering updates
|
||||||
console.log('Current value:', signal.valueOf());
|
console.log('Current value:', signal.valueOf());
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
You can also monitor signal changes by adding a listener:
|
You can also monitor signal changes by adding a listener:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -47,7 +47,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("h4", t`Debugging derived signals`),
|
el("h4", t`Debugging derived signals`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
With derived signals (created with ${el("code", "S(() => computation))")}), debugging is a bit more complex
|
With derived signals (created with ${el("code", "S(() => computation))")}), debugging is a bit more complex
|
||||||
because the value depends on other signals. To understand why a derived signal isn’t updating correctly:
|
because the value depends on other signals. To understand why a derived signal isn’t updating correctly:
|
||||||
`),
|
`),
|
||||||
@ -69,7 +69,7 @@ export function page({ pkg, info }){
|
|||||||
el(code, { src: fileURL("./components/examples/debugging/mutations.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/debugging/mutations.js"), page_id }),
|
||||||
|
|
||||||
el("h4", t`Memory leaks with signal listeners`),
|
el("h4", t`Memory leaks with signal listeners`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Signal listeners can cause memory leaks if not properly cleaned up. Always use AbortSignal
|
Signal listeners can cause memory leaks if not properly cleaned up. Always use AbortSignal
|
||||||
to cancel listeners.
|
to cancel listeners.
|
||||||
`),
|
`),
|
||||||
@ -84,12 +84,12 @@ export function page({ pkg, info }){
|
|||||||
el(code, { src: fileURL("./components/examples/debugging/debouncing.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/debugging/debouncing.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Browser DevTools tips for dd<el>`),
|
el(h3, t`Browser DevTools tips for dd<el>`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When debugging in the browser, dd<el> provides several helpful DevTools-friendly features:
|
When debugging in the browser, dd<el> provides several helpful DevTools-friendly features:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("h4", t`Identifying components in the DOM`),
|
el("h4", t`Identifying components in the DOM`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
dd<el> marks components in the DOM with special comment nodes to help you identify component boundaries.
|
dd<el> marks components in the DOM with special comment nodes to help you identify component boundaries.
|
||||||
Components created with ${el("code", "el(ComponentFunction)")} are marked with comment nodes
|
Components created with ${el("code", "el(ComponentFunction)")} are marked with comment nodes
|
||||||
${el("code", `<!--<dde:mark type="component" name="MyComponent" host="parentElement"/>-->`)} and
|
${el("code", `<!--<dde:mark type="component" name="MyComponent" host="parentElement"/>-->`)} and
|
||||||
@ -102,23 +102,23 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el("h4", t`Finding reactive elements in the DOM`),
|
el("h4", t`Finding reactive elements in the DOM`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When using ${el("code", "S.el()")}, dd<el> creates reactive elements in the DOM
|
When using ${el("code", "S.el()")}, dd<el> creates reactive elements in the DOM
|
||||||
that are automatically updated when signal values change. These elements are wrapped in special
|
that are automatically updated when signal values change. These elements are wrapped in special
|
||||||
comment nodes for debugging (to be true they are also used internally, so please do not edit them by hand):
|
comment nodes for debugging (to be true they are also used internally, so please do not edit them by hand):
|
||||||
`),
|
`),
|
||||||
el(code, { src: fileURL("./components/examples/debugging/dom-reactive-mark.html"), page_id }),
|
el(code, { src: fileURL("./components/examples/debugging/dom-reactive-mark.html"), page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This is particularly useful when debugging why a reactive section isn’t updating as expected.
|
This is particularly useful when debugging why a reactive section isn’t updating as expected.
|
||||||
You can inspect the elements between the comment nodes to see their current state and the
|
You can inspect the elements between the comment nodes to see their current state and the
|
||||||
signal connections through \`__dde_reactive\` of the host element.
|
signal connections through \`__dde_reactive\` of the host element.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("h4", t`DOM inspection properties`),
|
el("h4", t`DOM inspection properties`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Elements created with the dd<el> library have special properties to aid in debugging:
|
Elements created with the dd<el> library have special properties to aid in debugging:
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
${el("code", "<element>.__dde_reactive")} - An array property on DOM elements that tracks signal-to-element
|
${el("code", "<element>.__dde_reactive")} - An array property on DOM elements that tracks signal-to-element
|
||||||
relationships. This allows you to quickly identify which elements are reactive and what signals they’re
|
relationships. This allows you to quickly identify which elements are reactive and what signals they’re
|
||||||
bound to. Each entry in the array contains:
|
bound to. Each entry in the array contains:
|
||||||
@ -128,14 +128,14 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Additional context information about the element or attribute`),
|
el("li", t`Additional context information about the element or attribute`),
|
||||||
el("li", t`Automatically managed by signal.el(), signal.observedAttributes(), and processReactiveAttribute()`)
|
el("li", t`Automatically managed by signal.el(), signal.observedAttributes(), and processReactiveAttribute()`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
These properties make it easier to understand the reactive structure of your application when inspecting
|
These properties make it easier to understand the reactive structure of your application when inspecting
|
||||||
elements.
|
elements.
|
||||||
`),
|
`),
|
||||||
el(example, { src: fileURL("./components/examples/signals/debugging-dom.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/signals/debugging-dom.js"), page_id }),
|
||||||
|
|
||||||
el("h4", t`Examining signal connections`),
|
el("h4", t`Examining signal connections`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
${el("code", "<signal>.__dde_signal")} - A Symbol property used to identify and store the internal state of
|
${el("code", "<signal>.__dde_signal")} - A Symbol property used to identify and store the internal state of
|
||||||
signal objects. It contains the following information:
|
signal objects. It contains the following information:
|
||||||
`),
|
`),
|
||||||
@ -147,10 +147,10 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`defined: Stack trace information for debugging`),
|
el("li", t`defined: Stack trace information for debugging`),
|
||||||
el("li", t`readonly: Boolean flag indicating if the signal is read-only`)
|
el("li", t`readonly: Boolean flag indicating if the signal is read-only`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
…to determine the current value of the signal, call ${el("code", "signal.valueOf()")}.
|
…to determine the current value of the signal, call ${el("code", "signal.valueOf()")}.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
You can inspect (host) element relationships and bindings with signals in the DevTools console using
|
You can inspect (host) element relationships and bindings with signals in the DevTools console using
|
||||||
${el("code", "$0.__dde_reactive")} (for currently selected element). In the console you will see a list of
|
${el("code", "$0.__dde_reactive")} (for currently selected element). In the console you will see a list of
|
||||||
${el("code", `[ [ signal, listener ], element, property ]`)}, where:
|
${el("code", `[ [ signal, listener ], element, property ]`)}, where:
|
||||||
@ -161,26 +161,26 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`element — the DOM element that is bound to the signal`),
|
el("li", t`element — the DOM element that is bound to the signal`),
|
||||||
el("li", t`property — the attribute or property name which is changing based on the signal`),
|
el("li", t`property — the attribute or property name which is changing based on the signal`),
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
…the structure of \`__dde_reactive\` utilizes the browser’s behavior of packing the first field,
|
…the structure of \`__dde_reactive\` utilizes the browser’s behavior of packing the first field,
|
||||||
so you can see the element and property that changes in the console right away.
|
so you can see the element and property that changes in the console right away.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("h4", t`Debugging with breakpoints`),
|
el("h4", t`Debugging with breakpoints`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Effective use of breakpoints can help track signal flow:
|
Effective use of breakpoints can help track signal flow:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Set breakpoints in signal update methods to track when values change
|
Set breakpoints in signal update methods to track when values change
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Use conditional breakpoints to only break when specific signals change to certain values
|
Use conditional breakpoints to only break when specific signals change to certain values
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Set breakpoints in your signal computation functions to see when derived signals recalculate
|
Set breakpoints in your signal computation functions to see when derived signals recalculate
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Use performance profiling to identify bottlenecks in signal updates
|
Use performance profiling to identify bottlenecks in signal updates
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
@ -16,21 +16,21 @@ const fileURL= url=> new URL(url, import.meta.url);
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
dd<el> is designed with extensibility in mind. This page covers how to separate
|
dd<el> is designed with extensibility in mind. This page covers how to separate
|
||||||
third-party functionalities and integrate them seamlessly with the library, focusing on
|
third-party functionalities and integrate them seamlessly with the library, focusing on
|
||||||
proper resource cleanup and interoperability.
|
proper resource cleanup and interoperability.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`DOM Element Extensions with Addons`),
|
el(h3, t`DOM Element Extensions with Addons`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The primary method for extending DOM elements in dd<el> is through the Addon pattern.
|
The primary method for extending DOM elements in dd<el> is through the Addon pattern.
|
||||||
Addons are functions that take an element and applying some functionality to it. This pattern enables
|
Addons are functions that take an element and applying some functionality to it. This pattern enables
|
||||||
a clean, functional approach to element enhancement.
|
a clean, functional approach to element enhancement.
|
||||||
`),
|
`),
|
||||||
el("div", { className: "callout" }).append(
|
el("div", { className: "callout" }).append(
|
||||||
el("h4", t`What are Addons?`),
|
el("h4", t`What are Addons?`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Addons are simply functions with the signature: (element) => void. They:
|
Addons are simply functions with the signature: (element) => void. They:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -52,13 +52,13 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el(h3, t`Resource Cleanup with Abort Signals`),
|
el(h3, t`Resource Cleanup with Abort Signals`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When extending elements with functionality that uses resources like event listeners, timers,
|
When extending elements with functionality that uses resources like event listeners, timers,
|
||||||
or external subscriptions, it’s critical to clean up these resources when the element is removed
|
or external subscriptions, it’s critical to clean up these resources when the element is removed
|
||||||
from the DOM. dd<el> provides utilities for this through AbortSignal integration.
|
from the DOM. dd<el> provides utilities for this through AbortSignal integration.
|
||||||
`),
|
`),
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("code", "scope.signal")} property creates an AbortSignal that automatically
|
The ${el("code", "scope.signal")} property creates an AbortSignal that automatically
|
||||||
triggers when an element is disconnected from the DOM, making cleanup much easier to manage.
|
triggers when an element is disconnected from the DOM, making cleanup much easier to manage.
|
||||||
`)
|
`)
|
||||||
@ -86,7 +86,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el(h3, t`Building Library-Independent Extensions`),
|
el(h3, t`Building Library-Independent Extensions`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When creating extensions, it’s a good practice to make them as library-independent as possible.
|
When creating extensions, it’s a good practice to make them as library-independent as possible.
|
||||||
This approach enables better interoperability and future-proofing.
|
This approach enables better interoperability and future-proofing.
|
||||||
`),
|
`),
|
||||||
@ -125,13 +125,13 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Signal Extensions and Factory Patterns`),
|
el(h3, t`Signal Extensions and Factory Patterns`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Unlike DOM elements, signal functionality in dd<el> currently lacks a standardized
|
Unlike DOM elements, signal functionality in dd<el> currently lacks a standardized
|
||||||
way to create library-independent extensions. This is because signals are implemented
|
way to create library-independent extensions. This is because signals are implemented
|
||||||
differently across libraries.
|
differently across libraries.
|
||||||
`),
|
`),
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
In the future, JavaScript may include built-in signals through the
|
In the future, JavaScript may include built-in signals through the
|
||||||
${el("a", { href: "https://github.com/tc39/proposal-signals", textContent: "TC39 Signals Proposal" })}.
|
${el("a", { href: "https://github.com/tc39/proposal-signals", textContent: "TC39 Signals Proposal" })}.
|
||||||
dd<el> is designed with future compatibility in mind and will hopefully support these
|
dd<el> is designed with future compatibility in mind and will hopefully support these
|
||||||
@ -140,7 +140,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el("h4", t`The Signal Factory Pattern`),
|
el("h4", t`The Signal Factory Pattern`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
A powerful approach for extending signal functionality is the "Signal Factory" pattern.
|
A powerful approach for extending signal functionality is the "Signal Factory" pattern.
|
||||||
This approach encapsulates specific behavior in a function that creates and configures a signal.
|
This approach encapsulates specific behavior in a function that creates and configures a signal.
|
||||||
`),
|
`),
|
||||||
@ -191,13 +191,13 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Note how the factory accepts the signal constructor as a parameter, making it easier to test
|
Note how the factory accepts the signal constructor as a parameter, making it easier to test
|
||||||
and potentially migrate to different signal implementations in the future.
|
and potentially migrate to different signal implementations in the future.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("h4", t`Other Signal Extension Approaches`),
|
el("h4", t`Other Signal Extension Approaches`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
For simpler cases, you can also extend signals with clear interfaces and isolation to make
|
For simpler cases, you can also extend signals with clear interfaces and isolation to make
|
||||||
future migration easier.
|
future migration easier.
|
||||||
`),
|
`),
|
||||||
@ -221,7 +221,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When designing signal extensions, consider creating specialized signals for common patterns like:
|
When designing signal extensions, consider creating specialized signals for common patterns like:
|
||||||
forms, API requests, persistence, animations, or routing. These can significantly reduce
|
forms, API requests, persistence, animations, or routing. These can significantly reduce
|
||||||
boilerplate code in your applications.
|
boilerplate code in your applications.
|
||||||
@ -229,19 +229,19 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Using Signals Independently`),
|
el(h3, t`Using Signals Independently`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
While signals are tightly integrated with DDE’s DOM elements, you can also use them independently.
|
While signals are tightly integrated with DDE’s DOM elements, you can also use them independently.
|
||||||
This can be useful when you need reactivity in non-UI code or want to integrate with other libraries.
|
This can be useful when you need reactivity in non-UI code or want to integrate with other libraries.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
There are two ways to import signals:
|
There are two ways to import signals:
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Standard import")}: ${el("code", `import { S } from "deka-dom-el/signals";`)}
|
${el("strong", "Standard import")}: ${el("code", `import { S } from "deka-dom-el/signals";`)}
|
||||||
— This automatically registers signals with DDE’s DOM reactivity system
|
— This automatically registers signals with DDE’s DOM reactivity system
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Independent import")}: ${el("code", `import { S } from "deka-dom-el/src/signals-lib";`)}
|
${el("strong", "Independent import")}: ${el("code", `import { S } from "deka-dom-el/src/signals-lib";`)}
|
||||||
— This gives you just the signal system without DOM integration
|
— This gives you just the signal system without DOM integration
|
||||||
`)
|
`)
|
||||||
@ -261,7 +261,7 @@ export function page({ pkg, info }){
|
|||||||
count.set(5); // Logs: 5
|
count.set(5); // Logs: 5
|
||||||
console.log(doubled.get()); // 10
|
console.log(doubled.get()); // 10
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The independent signals API includes all core functionality (${el("code", "S()")}, ${el("code", "S.on()")},
|
The independent signals API includes all core functionality (${el("code", "S()")}, ${el("code", "S.on()")},
|
||||||
${el("code", "S.action()")}).
|
${el("code", "S.action()")}).
|
||||||
`),
|
`),
|
||||||
@ -276,27 +276,27 @@ export function page({ pkg, info }){
|
|||||||
|
|
||||||
el(h3, t`Best Practices for Extensions`),
|
el(h3, t`Best Practices for Extensions`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Use AbortSignals for cleanup:")} Always implement proper resource cleanup with
|
${el("strong", "Use AbortSignals for cleanup:")} Always implement proper resource cleanup with
|
||||||
${el("code", "scope.signal")} or similar mechanisms
|
${el("code", "scope.signal")} or similar mechanisms
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Separate core logic from library adaptation:")} Make your core functionality work
|
${el("strong", "Separate core logic from library adaptation:")} Make your core functionality work
|
||||||
with standard DOM APIs when possible
|
with standard DOM APIs when possible
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Use signal factories for common patterns:")} Create reusable signal factories that encapsulate
|
${el("strong", "Use signal factories for common patterns:")} Create reusable signal factories that encapsulate
|
||||||
domain-specific behavior and state logic
|
domain-specific behavior and state logic
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Document clearly:")} Provide clear documentation on how your extension works
|
${el("strong", "Document clearly:")} Provide clear documentation on how your extension works
|
||||||
and what resources it uses
|
and what resources it uses
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Follow the Addon pattern:")} Keep to the (element) => element signature for
|
${el("strong", "Follow the Addon pattern:")} Keep to the (element) => element signature for
|
||||||
DOM element extensions
|
DOM element extensions
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Avoid modifying global state:")} Extensions should be self-contained and not
|
${el("strong", "Avoid modifying global state:")} Extensions should be self-contained and not
|
||||||
affect other parts of the application
|
affect other parts of the application
|
||||||
`)
|
`)
|
||||||
|
@ -45,7 +45,7 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
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
|
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
|
techniques to optimize rendering performance, especially when dealing with large lists or frequently
|
||||||
updating components. This guide focuses on memoization and other optimization strategies.
|
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(code, { src: fileURL("./components/examples/optimization/intro.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Memoization with memo: Native vs dd<el>`),
|
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
|
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
|
or relying on complex virtual DOM diffing algorithms. dd<el>'s ${el("code", "memo")} function
|
||||||
provides a simpler, more direct approach:
|
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
|
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
|
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.
|
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:
|
The memo system is particularly useful for:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -122,7 +122,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Using memo with Signal Rendering`),
|
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
|
The most common use case for memoization is within ${el("code", "S.el()")} when rendering lists with
|
||||||
${el("code", "map()")}:
|
${el("code", "map()")}:
|
||||||
`),
|
`),
|
||||||
@ -136,7 +136,7 @@ export function page({ pkg, info }){
|
|||||||
))))
|
))))
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("code", "memo")} function in this context:
|
The ${el("code", "memo")} function in this context:
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
@ -149,7 +149,7 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/optimization/memo.js"), page_id }),
|
el(example, { src: fileURL("./components/examples/optimization/memo.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Creating Memoization Scopes`),
|
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.
|
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:
|
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 }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The scope function accepts options to customize its behavior:
|
The scope function accepts options to customize its behavior:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -193,12 +193,12 @@ export function page({ pkg, info }){
|
|||||||
el("div", { className: "function-table" }).append(
|
el("div", { className: "function-table" }).append(
|
||||||
el("dl").append(
|
el("dl").append(
|
||||||
el("dt", t`onlyLast Option`),
|
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 ")
|
which is useful when the entire collection is replaced. ${el("strong", "This is default behavior of ")
|
||||||
.append(el("code", "S.el"))}!`),
|
.append(el("code", "S.el"))}!`),
|
||||||
|
|
||||||
el("dt", t`signal Option`),
|
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`)
|
that will clear the cache when aborted, helping with memory management`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -206,7 +206,7 @@ export function page({ pkg, info }){
|
|||||||
el(h3, t`Additional Optimization Techniques`),
|
el(h3, t`Additional Optimization Techniques`),
|
||||||
|
|
||||||
el("h4", t`Minimizing Signal Updates`),
|
el("h4", t`Minimizing Signal Updates`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Signals are efficient, but unnecessary updates can impact performance:
|
Signals are efficient, but unnecessary updates can impact performance:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -217,7 +217,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el("h4", t`Optimizing List Rendering`),
|
el("h4", t`Optimizing List Rendering`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Beyond memoization, consider these approaches for optimizing list rendering:
|
Beyond memoization, consider these approaches for optimizing list rendering:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -228,17 +228,17 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
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
|
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.
|
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 aren’t enough.
|
Alternatively you can use any “jsonable” value as key, when the primitive values aren’t enough.
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("h4", t`Memory Management`),
|
el("h4", t`Memory Management`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
To prevent memory leaks and reduce memory consumption:
|
To prevent memory leaks and reduce memory consumption:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -249,7 +249,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el("h4", t`Choosing the Right Optimization Approach`),
|
el("h4", t`Choosing the Right Optimization Approach`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
While memo is powerful, it's not always the best solution:
|
While memo is powerful, it's not always the best solution:
|
||||||
`),
|
`),
|
||||||
el("table").append(
|
el("table").append(
|
||||||
@ -280,12 +280,12 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Known Issues and Limitations`),
|
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:
|
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("h4", t`Document Fragments and Memoization`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
One important limitation to understand is how memoization interacts with
|
One important limitation to understand is how memoization interacts with
|
||||||
${el("a", references.mdn_fragment).append("DocumentFragment")} objects.
|
${el("a", references.mdn_fragment).append("DocumentFragment")} objects.
|
||||||
Functions like ${el("code", "S.el")} internally use DocumentFragment to efficiently handle multiple elements,
|
Functions like ${el("code", "S.el")} internally use DocumentFragment to efficiently handle multiple elements,
|
||||||
@ -305,7 +305,7 @@ export function page({ pkg, info }){
|
|||||||
container.append(memoizedFragment); // Nothing gets appended
|
container.append(memoizedFragment); // Nothing gets appended
|
||||||
`, page_id }),
|
`, 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
|
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.
|
is cached by memo and reused, it's already empty.
|
||||||
`),
|
`),
|
||||||
@ -327,7 +327,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id })
|
`, page_id })
|
||||||
),
|
),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Generally, you should either:
|
Generally, you should either:
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
@ -337,7 +337,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
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.
|
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,
|
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.
|
leaving the original fragment empty.
|
||||||
@ -345,11 +345,11 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Performance Debugging`),
|
el(h3, t`Performance Debugging`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
To identify performance bottlenecks in your dd<el> applications:
|
To identify performance bottlenecks in your dd<el> applications:
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
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`),
|
rendering times`),
|
||||||
el("li", t`Check for excessive signal updates using S.on() listeners with console.log`),
|
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`),
|
el("li", t`Verify memo usage by inspecting cache hit rates`),
|
||||||
@ -357,26 +357,26 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
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.
|
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(h3, t`Best Practices for Optimized Rendering`),
|
||||||
el("ol").append(
|
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("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("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("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("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
|
${el("strong", "Avoid memoizing fragments:")} Memoize individual elements or use container elements
|
||||||
instead of DocumentFragments.
|
instead of DocumentFragments.
|
||||||
`)
|
`)
|
||||||
|
@ -45,7 +45,7 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
${el("a", references.todomvc).append("TodoMVC")} is a project that helps developers compare different
|
${el("a", references.todomvc).append("TodoMVC")} is a project that helps developers compare different
|
||||||
frameworks by implementing the same todo application. This implementation showcases how dd<el>
|
frameworks by implementing the same todo application. This implementation showcases how dd<el>
|
||||||
can be used to build a complete, real-world application with all the expected features of a modern
|
can be used to build a complete, real-world application with all the expected features of a modern
|
||||||
@ -63,7 +63,7 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Component scopes for proper encapsulation`)
|
el("li", t`Component scopes for proper encapsulation`)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Below is a fully working TodoMVC implementation. You can interact with it directly in this
|
Below is a fully working TodoMVC implementation. You can interact with it directly in this
|
||||||
documentation page. The example demonstrates how dd<el> handles common app development
|
documentation page. The example demonstrates how dd<el> handles common app development
|
||||||
challenges in a clean, maintainable way.
|
challenges in a clean, maintainable way.
|
||||||
@ -72,7 +72,7 @@ export function page({ pkg, info }){
|
|||||||
el(example, { src: fileURL("./components/examples/reallife/todomvc.js"), variant: "big", page_id }),
|
el(example, { src: fileURL("./components/examples/reallife/todomvc.js"), variant: "big", page_id }),
|
||||||
|
|
||||||
el(h3, t`Application Architecture Overview`),
|
el(h3, t`Application Architecture Overview`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The TodoMVC implementation is structured around several key components:
|
The TodoMVC implementation is structured around several key components:
|
||||||
`),
|
`),
|
||||||
el("div", { className: "function-table" }).append(
|
el("div", { className: "function-table" }).append(
|
||||||
@ -96,7 +96,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Reactive State Management with Signals`),
|
el(h3, t`Reactive State Management with Signals`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The application uses three primary signals to manage state:
|
The application uses three primary signals to manage state:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -118,7 +118,7 @@ export function page({ pkg, info }){
|
|||||||
});
|
});
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("code", "todosSignal")} function creates a custom signal with actions for manipulating the todos:
|
The ${el("code", "todosSignal")} function creates a custom signal with actions for manipulating the todos:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -200,7 +200,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Using ${el("a", references.mdn_storage).append("localStorage")} allows the application to persist todos
|
Using ${el("a", references.mdn_storage).append("localStorage")} allows the application to persist todos
|
||||||
even when the page is refreshed. The ${el("code", "S.on")} listener ensures todos are saved
|
even when the page is refreshed. The ${el("code", "S.on")} listener ensures todos are saved
|
||||||
after every state change, providing automatic persistence without explicit calls.
|
after every state change, providing automatic persistence without explicit calls.
|
||||||
@ -208,7 +208,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Integration of Signals and Reactive UI`),
|
el(h3, t`Integration of Signals and Reactive UI`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The implementation demonstrates a clean integration between signal state and reactive UI:
|
The implementation demonstrates a clean integration between signal state and reactive UI:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The derived signal automatically recalculates whenever either the todos list or the current filter changes,
|
The derived signal automatically recalculates whenever either the todos list or the current filter changes,
|
||||||
ensuring the UI always shows the correct filtered todos.
|
ensuring the UI always shows the correct filtered todos.
|
||||||
`),
|
`),
|
||||||
@ -268,7 +268,7 @@ export function page({ pkg, info }){
|
|||||||
}
|
}
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The TodoItem component maintains its own local UI state with signals, providing immediate
|
The TodoItem component maintains its own local UI state with signals, providing immediate
|
||||||
UI feedback while still communicating changes to the parent via events.
|
UI feedback while still communicating changes to the parent via events.
|
||||||
`),
|
`),
|
||||||
@ -289,14 +289,14 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Binding signals directly to element properties creates a reactive UI that automatically updates
|
Binding signals directly to element properties creates a reactive UI that automatically updates
|
||||||
when state changes, without the need for explicit DOM manipulation or virtual DOM diffing.
|
when state changes, without the need for explicit DOM manipulation or virtual DOM diffing.
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Performance Optimization with Memoization`),
|
el(h3, t`Performance Optimization with Memoization`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The implementation uses ${el("code", "memo")} to optimize performance in several key areas:
|
The implementation uses ${el("code", "memo")} to optimize performance in several key areas:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This approach ensures that:
|
This approach ensures that:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -329,14 +329,14 @@ export function page({ pkg, info }){
|
|||||||
))
|
))
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
By memoizing based on the todos length, the entire footer component is only re-rendered
|
By memoizing based on the todos length, the entire footer component is only re-rendered
|
||||||
when todos are added or removed, not when their properties change. This improves performance
|
when todos are added or removed, not when their properties change. This improves performance
|
||||||
by avoiding unnecessary DOM operations.
|
by avoiding unnecessary DOM operations.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Memoization is especially important for UI elements that are expensive to render or that contain
|
Memoization is especially important for UI elements that are expensive to render or that contain
|
||||||
many child elements. The ${el("code", "memo")} function allows precise control over when components
|
many child elements. The ${el("code", "memo")} function allows precise control over when components
|
||||||
should re-render, avoiding the overhead of virtual DOM diffing algorithms.
|
should re-render, avoiding the overhead of virtual DOM diffing algorithms.
|
||||||
@ -344,13 +344,13 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Component-Based Architecture with Events`),
|
el(h3, t`Component-Based Architecture with Events`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The TodoMVC implementation demonstrates a clean component architecture with custom events
|
The TodoMVC implementation demonstrates a clean component architecture with custom events
|
||||||
for communication between components:
|
for communication between components:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("h4", t`1. Main Component Event Handling`),
|
el("h4", t`1. Main Component Event Handling`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The main Todos component sets up event listeners to handle actions from child components:
|
The main Todos component sets up event listeners to handle actions from child components:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -360,7 +360,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("h4", t`2. The TodoItem Component with Scopes and Local State`),
|
el("h4", t`2. The TodoItem Component with Scopes and Local State`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Each todo item is rendered by the TodoItem component that uses scopes, local signals, and custom events:
|
Each todo item is rendered by the TodoItem component that uses scopes, local signals, and custom events:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -403,7 +403,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Using ${el("code", "scope")} and ${el("a", references.mdn_events).append("custom events")}
|
Using ${el("code", "scope")} and ${el("a", references.mdn_events).append("custom events")}
|
||||||
creates a clean separation of concerns. Each TodoItem component dispatches events up to the parent
|
creates a clean separation of concerns. Each TodoItem component dispatches events up to the parent
|
||||||
without directly manipulating the application state, following a unidirectional data flow pattern.
|
without directly manipulating the application state, following a unidirectional data flow pattern.
|
||||||
@ -411,7 +411,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Improved DOM Updates with classList`),
|
el(h3, t`Improved DOM Updates with classList`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The implementation uses the reactive ${el("code", "classList")} property for efficient class updates:
|
The implementation uses the reactive ${el("code", "classList")} property for efficient class updates:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -423,7 +423,7 @@ export function page({ pkg, info }){
|
|||||||
);
|
);
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Benefits of using ${el("code", "classList")}:
|
Benefits of using ${el("code", "classList")}:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -434,7 +434,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Improved Focus Management`),
|
el(h3, t`Improved Focus Management`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The implementation uses a dedicated function for managing focus in edit inputs:
|
The implementation uses a dedicated function for managing focus in edit inputs:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -462,7 +462,7 @@ export function page({ pkg, info }){
|
|||||||
}, onBlurEdit, onKeyDown, addFocus)
|
}, onBlurEdit, onKeyDown, addFocus)
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This approach offers several advantages:
|
This approach offers several advantages:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -473,7 +473,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Using ${el("a", references.mdn_raf).append("requestAnimationFrame")} ensures that the focus operation
|
Using ${el("a", references.mdn_raf).append("requestAnimationFrame")} ensures that the focus operation
|
||||||
happens after the browser has finished rendering the DOM changes, which is more reliable than
|
happens after the browser has finished rendering the DOM changes, which is more reliable than
|
||||||
using setTimeout.
|
using setTimeout.
|
||||||
@ -481,7 +481,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Efficient Conditional Rendering`),
|
el(h3, t`Efficient Conditional Rendering`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The implementation uses signals for efficient conditional rendering:
|
The implementation uses signals for efficient conditional rendering:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Unlike frameworks that use a virtual DOM, dd<el> directly updates only the specific DOM elements
|
Unlike frameworks that use a virtual DOM, dd<el> directly updates only the specific DOM elements
|
||||||
that need to change. This approach is often more efficient for small to medium-sized applications,
|
that need to change. This approach is often more efficient for small to medium-sized applications,
|
||||||
especially when combined with strategic memoization.
|
especially when combined with strategic memoization.
|
||||||
@ -528,7 +528,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Type Safety with JSDoc Comments`),
|
el(h3, t`Type Safety with JSDoc Comments`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The implementation uses comprehensive JSDoc comments to provide type safety without requiring TypeScript:
|
The implementation uses comprehensive JSDoc comments to provide type safety without requiring TypeScript:
|
||||||
`),
|
`),
|
||||||
el(code, { content: `
|
el(code, { content: `
|
||||||
@ -566,7 +566,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("div", { className: "tip" }).append(
|
el("div", { className: "tip" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Using JSDoc comments provides many of the benefits of TypeScript (autocomplete, type checking,
|
Using JSDoc comments provides many of the benefits of TypeScript (autocomplete, type checking,
|
||||||
documentation) while maintaining pure JavaScript code. This approach works well with modern
|
documentation) while maintaining pure JavaScript code. This approach works well with modern
|
||||||
IDEs that support JSDoc type inference.
|
IDEs that support JSDoc type inference.
|
||||||
@ -575,41 +575,41 @@ export function page({ pkg, info }){
|
|||||||
|
|
||||||
el(h3, t`Best Practices Demonstrated`),
|
el(h3, t`Best Practices Demonstrated`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Component Composition:")} Breaking the UI into focused, reusable components
|
${el("strong", "Component Composition:")} Breaking the UI into focused, reusable components
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Performance Optimization:")} Strategic memoization to minimize DOM operations
|
${el("strong", "Performance Optimization:")} Strategic memoization to minimize DOM operations
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Reactive State Management:")} Using signals with derived computations
|
${el("strong", "Reactive State Management:")} Using signals with derived computations
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Event-Based Communication:")} Using custom events for component communication
|
${el("strong", "Event-Based Communication:")} Using custom events for component communication
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Local Component State:")} Maintaining UI state within components for better encapsulation
|
${el("strong", "Local Component State:")} Maintaining UI state within components for better encapsulation
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Declarative Class Management:")} Using the classList property for cleaner class handling
|
${el("strong", "Declarative Class Management:")} Using the classList property for cleaner class handling
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Focus Management:")} Reliable input focus with requestAnimationFrame
|
${el("strong", "Focus Management:")} Reliable input focus with requestAnimationFrame
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Persistent Storage:")} Automatically saving application state with signal listeners
|
${el("strong", "Persistent Storage:")} Automatically saving application state with signal listeners
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Type Safety:")} Using comprehensive JSDoc comments for type checking and documentation
|
${el("strong", "Type Safety:")} Using comprehensive JSDoc comments for type checking and documentation
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Composable Event Handlers:")} Attaching multiple event handlers to elements
|
${el("strong", "Composable Event Handlers:")} Attaching multiple event handlers to elements
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("div", { className: "callout" }).append(
|
el("div", { className: "callout" }).append(
|
||||||
el("h4", t`Key Takeaways`),
|
el("h4", t`Key Takeaways`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This TodoMVC implementation showcases the strengths of dd<el> for building real-world applications:
|
This TodoMVC implementation showcases the strengths of dd<el> for building real-world applications:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -622,7 +622,7 @@ export function page({ pkg, info }){
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
You can find the ${el("a", references.github_example).append("complete source code")} for this example on GitHub.
|
You can find the ${el("a", references.github_example).append("complete source code")} for this example on GitHub.
|
||||||
Feel free to use it as a reference for your own projects or as a starting point for more complex applications.
|
Feel free to use it as a reference for your own projects or as a starting point for more complex applications.
|
||||||
`),
|
`),
|
||||||
|
@ -17,7 +17,7 @@ export function page({ pkg, info }){
|
|||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("div", { className: "warning" }).append(
|
el("div", { className: "warning" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This part of the documentation is primarily intended for technical enthusiasts and authors of
|
This part of the documentation is primarily intended for technical enthusiasts and authors of
|
||||||
3rd-party libraries. It describes an advanced feature, not a core part of the library. Most users will
|
3rd-party libraries. It describes an advanced feature, not a core part of the library. Most users will
|
||||||
not need to implement this functionality directly in their applications. This capability will hopefully
|
not need to implement this functionality directly in their applications. This capability will hopefully
|
||||||
@ -25,21 +25,21 @@ export function page({ pkg, info }){
|
|||||||
dd<el>.
|
dd<el>.
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
dd<el> isn’t limited to browser environments. Thanks to its flexible architecture,
|
dd<el> isn’t limited to browser environments. Thanks to its flexible architecture,
|
||||||
it can be used for server-side rendering (SSR) to generate static HTML files.
|
it can be used for server-side rendering (SSR) to generate static HTML files.
|
||||||
This is achieved through integration with for example ${el("a", { href: "https://github.com/tmpvar/jsdom",
|
This is achieved through integration with for example ${el("a", { href: "https://github.com/tmpvar/jsdom",
|
||||||
textContent: "jsdom" })}, a JavaScript implementation of web standards for Node.js.
|
textContent: "jsdom" })}, a JavaScript implementation of web standards for Node.js.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Additionally, you might consider using these alternative solutions:
|
Additionally, you might consider using these alternative solutions:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("a", { href: "https://github.com/capricorn86/happy-dom", textContent: "happy-dom" })} —
|
${el("a", { href: "https://github.com/capricorn86/happy-dom", textContent: "happy-dom" })} —
|
||||||
A JavaScript implementation of a web browser without its graphical user interface that’s faster than jsdom
|
A JavaScript implementation of a web browser without its graphical user interface that’s faster than jsdom
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("a", { href: "https://github.com/WebReflection/linkedom", textContent: "linkedom" })} —
|
${el("a", { href: "https://github.com/WebReflection/linkedom", textContent: "linkedom" })} —
|
||||||
A lightweight DOM implementation specifically designed for SSR with significantly better performance
|
A lightweight DOM implementation specifically designed for SSR with significantly better performance
|
||||||
than jsdom
|
than jsdom
|
||||||
@ -48,7 +48,7 @@ export function page({ pkg, info }){
|
|||||||
el(code, { src: fileURL("./components/examples/ssr/intro.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/ssr/intro.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Why Server-Side Rendering?`),
|
el(h3, t`Why Server-Side Rendering?`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
SSR offers several benefits:
|
SSR offers several benefits:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -60,7 +60,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`How jsdom Integration Works`),
|
el(h3, t`How jsdom Integration Works`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The jsdom export in dd<el> provides the necessary tools to use the library in Node.js
|
The jsdom export in dd<el> provides the necessary tools to use the library in Node.js
|
||||||
by integrating with jsdom. Here’s what it does:
|
by integrating with jsdom. Here’s what it does:
|
||||||
`),
|
`),
|
||||||
@ -74,55 +74,55 @@ export function page({ pkg, info }){
|
|||||||
el(code, { src: fileURL("./components/examples/ssr/start.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/ssr/start.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Basic SSR Example`),
|
el(h3, t`Basic SSR Example`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Here’s a simple example of how to use dd<el> for server-side rendering in a Node.js script:
|
Here’s a simple example of how to use dd<el> for server-side rendering in a Node.js script:
|
||||||
`),
|
`),
|
||||||
el(code, { src: fileURL("./components/examples/ssr/basic-example.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/ssr/basic-example.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Building a Static Site Generator`),
|
el(h3, t`Building a Static Site Generator`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
You can build a complete static site generator with dd<el>. In fact, this documentation site
|
You can build a complete static site generator with dd<el>. In fact, this documentation site
|
||||||
is built using dd<el> for server-side rendering! Here’s how the documentation build process works:
|
is built using dd<el> for server-side rendering! Here’s how the documentation build process works:
|
||||||
`),
|
`),
|
||||||
el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Working with Async Content in SSR`),
|
el(h3, t`Working with Async Content in SSR`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The jsdom export includes a queue system to handle asynchronous operations during rendering.
|
The jsdom export includes a queue system to handle asynchronous operations during rendering.
|
||||||
This is crucial for components that fetch data or perform other async tasks.
|
This is crucial for components that fetch data or perform other async tasks.
|
||||||
`),
|
`),
|
||||||
el(code, { src: fileURL("./components/examples/ssr/async-data.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/ssr/async-data.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`Working with Dynamic Imports for SSR`),
|
el(h3, t`Working with Dynamic Imports for SSR`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When structuring server-side rendering code, a crucial pattern to follow is using dynamic imports
|
When structuring server-side rendering code, a crucial pattern to follow is using dynamic imports
|
||||||
for both the deka-dom-el/jsdom module and your page components.
|
for both the deka-dom-el/jsdom module and your page components.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Why is this important?
|
Why is this important?
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Static imports are hoisted:")} JavaScript hoists import statements to the top of the file,
|
${el("strong", "Static imports are hoisted:")} JavaScript hoists import statements to the top of the file,
|
||||||
executing them before any other code
|
executing them before any other code
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Environment registration timing:")} The jsdom module auto-registers the DOM environment
|
${el("strong", "Environment registration timing:")} The jsdom module auto-registers the DOM environment
|
||||||
when imported, which must happen ${el("em", "after")} you’ve created your JSDOM instance and
|
when imported, which must happen ${el("em", "after")} you’ve created your JSDOM instance and
|
||||||
${el("em", "before")} you import your components using ${el("code", "import { el } from \"deka-dom-el\";")}.
|
${el("em", "before")} you import your components using ${el("code", "import { el } from \"deka-dom-el\";")}.
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Correct initialization order:")} You need to control the exact sequence of:
|
${el("strong", "Correct initialization order:")} You need to control the exact sequence of:
|
||||||
create JSDOM → register environment → import components
|
create JSDOM → register environment → import components
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Follow this pattern when creating server-side rendered pages:
|
Follow this pattern when creating server-side rendered pages:
|
||||||
`),
|
`),
|
||||||
el(code, { src: fileURL("./components/examples/ssr/pages.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/ssr/pages.js"), page_id }),
|
||||||
|
|
||||||
el(h3, t`SSR Considerations and Limitations`),
|
el(h3, t`SSR Considerations and Limitations`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When using dd<el> for SSR, keep these considerations in mind:
|
When using dd<el> for SSR, keep these considerations in mind:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -131,19 +131,19 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Some DOM features may behave differently in jsdom compared to real browsers`),
|
el("li", t`Some DOM features may behave differently in jsdom compared to real browsers`),
|
||||||
el("li", t`For large sites, you may need to optimize memory usage by creating a new jsdom instance for each page`)
|
el("li", t`For large sites, you may need to optimize memory usage by creating a new jsdom instance for each page`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
For advanced SSR applications, consider implementing hydration on the client-side to restore
|
For advanced SSR applications, consider implementing hydration on the client-side to restore
|
||||||
interactivity after the initial render.
|
interactivity after the initial render.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Real Example: How This Documentation is Built`),
|
el(h3, t`Real Example: How This Documentation is Built`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This documentation site itself is built using dd<el>’s SSR capabilities.
|
This documentation site itself is built using dd<el>’s SSR capabilities.
|
||||||
The build process collects all page components, renders them with jsdom, and outputs static HTML files.
|
The build process collects all page components, renders them with jsdom, and outputs static HTML files.
|
||||||
`),
|
`),
|
||||||
el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
|
el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The resulting static files can be deployed to any static hosting service,
|
The resulting static files can be deployed to any static hosting service,
|
||||||
providing fast loading times and excellent SEO without the need for client-side JavaScript
|
providing fast loading times and excellent SEO without the need for client-side JavaScript
|
||||||
to render the initial content.
|
to render the initial content.
|
||||||
|
@ -19,7 +19,7 @@ export function page({ pkg, info }){
|
|||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("div", { className: "warning" }).append(
|
el("div", { className: "warning" }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This part of the documentation is primarily intended for technical enthusiasts and authors of
|
This part of the documentation is primarily intended for technical enthusiasts and authors of
|
||||||
3rd-party libraries. It describes an advanced feature, not a core part of the library. Most users will
|
3rd-party libraries. It describes an advanced feature, not a core part of the library. Most users will
|
||||||
not need to implement this functionality directly in their applications. This capability will hopefully
|
not need to implement this functionality directly in their applications. This capability will hopefully
|
||||||
@ -29,7 +29,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`What Are Ireland Components?`),
|
el(h3, t`What Are Ireland Components?`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Ireland components are a special type of component that:
|
Ireland components are a special type of component that:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
@ -38,24 +38,24 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`How Ireland Components Work`),
|
el(h3, t`How Ireland Components Work`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The Ireland component system consists of several parts working together:
|
The Ireland component system consists of several parts working together:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Server-side rendering:")} Components are pre-rendered during the documentation build process
|
${el("strong", "Server-side rendering:")} Components are pre-rendered during the documentation build process
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Component registration:")} Source files are copied to the documentation output directory
|
${el("strong", "Component registration:")} Source files are copied to the documentation output directory
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Client-side scripting:")} JavaScript code is generated to load and render components
|
${el("strong", "Client-side scripting:")} JavaScript code is generated to load and render components
|
||||||
`),
|
`),
|
||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Implementation Architecture`),
|
el(h3, t`Implementation Architecture`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The core of the Ireland system is implemented in ${el("code", "docs/components/ireland.html.js")}.
|
The core of the Ireland system is implemented in ${el("code", "docs/components/ireland.html.js")}.
|
||||||
It integrates with the SSR build process using the ${el("code", "registerClientFile")} function
|
It integrates with the SSR build process using the ${el("code", "registerClientFile")} function
|
||||||
from ${el("code", "docs/ssr.js")}.
|
from ${el("code", "docs/ssr.js")}.
|
||||||
@ -69,7 +69,7 @@ export function page({ pkg, info }){
|
|||||||
})
|
})
|
||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
During the build process (${el("code", "bs/docs.js")}), the following happens:
|
During the build process (${el("code", "bs/docs.js")}), the following happens:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Core Implementation Details`),
|
el(h3, t`Core Implementation Details`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Let's look at the key parts of the ireland component implementation:
|
Let's look at the key parts of the ireland component implementation:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
@ -253,7 +253,7 @@ export function page({ pkg, info }){
|
|||||||
`, page_id }),
|
`, page_id }),
|
||||||
|
|
||||||
el(h3, t`Live Example`),
|
el(h3, t`Live Example`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Here’s a live example of an Ireland component showing a standard counter.
|
Here’s a live example of an Ireland component showing a standard counter.
|
||||||
The component is defined in ${el("code", "docs/components/examples/ireland-test/counter.js")} and
|
The component is defined in ${el("code", "docs/components/examples/ireland-test/counter.js")} and
|
||||||
rendered with the Ireland component system:
|
rendered with the Ireland component system:
|
||||||
@ -269,21 +269,21 @@ export function page({ pkg, info }){
|
|||||||
page_id
|
page_id
|
||||||
}),
|
}),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When the page is loaded, the component is also loaded and rendered. The counter state is maintained
|
When the page is loaded, the component is also loaded and rendered. The counter state is maintained
|
||||||
using signals, allowing for reactive updates as you click the buttons to increment and decrement the
|
using signals, allowing for reactive updates as you click the buttons to increment and decrement the
|
||||||
value.
|
value.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Practical Considerations and Limitations`),
|
el(h3, t`Practical Considerations and Limitations`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When implementing Ireland components in real documentation, there are several important
|
When implementing Ireland components in real documentation, there are several important
|
||||||
considerations to keep in mind:
|
considerations to keep in mind:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el("div", { className: "warning" }).append(
|
el("div", { className: "warning" }).append(
|
||||||
el("h4", t`Module Resolution and Bundling`),
|
el("h4", t`Module Resolution and Bundling`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The examples shown here use bare module specifiers like ${el("code",
|
The examples shown here use bare module specifiers like ${el("code",
|
||||||
`import { el } from "deka-dom-el"`)} which aren’t supported in all browsers without importmaps.
|
`import { el } from "deka-dom-el"`)} which aren’t supported in all browsers without importmaps.
|
||||||
In a production implementation, you would need to: `),
|
In a production implementation, you would need to: `),
|
||||||
@ -292,7 +292,7 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Bundle component dependencies to avoid multiple requests`),
|
el("li", t`Bundle component dependencies to avoid multiple requests`),
|
||||||
el("li", t`Ensure all module dependencies are properly resolved and copied to the output directory`)
|
el("li", t`Ensure all module dependencies are properly resolved and copied to the output directory`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
In this documentation, we replace the paths with ${el("code", "./esm-with-signals.js")} and provide
|
In this documentation, we replace the paths with ${el("code", "./esm-with-signals.js")} and provide
|
||||||
a bundled version of the library, but more complex components might require a dedicated bundling step.
|
a bundled version of the library, but more complex components might require a dedicated bundling step.
|
||||||
`)
|
`)
|
||||||
@ -300,7 +300,7 @@ export function page({ pkg, info }){
|
|||||||
|
|
||||||
el("div", { className: "note" }).append(
|
el("div", { className: "note" }).append(
|
||||||
el("h4", t`Component Dependencies`),
|
el("h4", t`Component Dependencies`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Real-world components typically depend on multiple modules and assets. The Ireland system would need
|
Real-world components typically depend on multiple modules and assets. The Ireland system would need
|
||||||
to be extended to:
|
to be extended to:
|
||||||
`),
|
`),
|
||||||
@ -312,7 +312,7 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Advanced Usage`),
|
el(h3, t`Advanced Usage`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The Ireland system can be extended in several ways to address these limitations:
|
The Ireland system can be extended in several ways to address these limitations:
|
||||||
`),
|
`),
|
||||||
|
|
||||||
@ -325,7 +325,7 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Implement state persistence between runs`)
|
el("li", t`Implement state persistence between runs`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This documentation site itself is built using the techniques described here,
|
This documentation site itself is built using the techniques described here,
|
||||||
showcasing how dd<el> can be used to create both the documentation and
|
showcasing how dd<el> can be used to create both the documentation and
|
||||||
the interactive examples within it. The implementation here is simplified for clarity,
|
the interactive examples within it. The implementation here is simplified for clarity,
|
||||||
|
@ -32,45 +32,45 @@ const references= {
|
|||||||
export function page({ pkg, info }){
|
export function page({ pkg, info }){
|
||||||
const page_id= info.id;
|
const page_id= info.id;
|
||||||
return el(simplePage, { info, pkg }).append(
|
return el(simplePage, { info, pkg }).append(
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
This reference guide provides a comprehensive summary of dd<el>'s key concepts, best practices,
|
This reference guide provides a comprehensive summary of dd<el>'s key concepts, best practices,
|
||||||
case studies, and advanced techniques. Use it as a quick reference when working with the library
|
case studies, and advanced techniques. Use it as a quick reference when working with the library
|
||||||
or to deepen your understanding of its design principles and patterns.
|
or to deepen your understanding of its design principles and patterns.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
el(h3, t`Core Principles of dd<el>`),
|
el(h3, t`Core Principles of dd<el>`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
At its foundation, dd<el> is built on several core principles that shape its design and usage:
|
At its foundation, dd<el> is built on several core principles that shape its design and usage:
|
||||||
`),
|
`),
|
||||||
el("div", { className: "callout" }).append(
|
el("div", { className: "callout" }).append(
|
||||||
el("h4", t`Guiding Principles`),
|
el("h4", t`Guiding Principles`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "DOM-First Approach:")} Working directly with the real DOM instead of virtual DOM
|
${el("strong", "DOM-First Approach:")} Working directly with the real DOM instead of virtual DOM
|
||||||
abstractions
|
abstractions
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Declarative Syntax:")} Creating UIs by describing what they should look like, not
|
${el("strong", "Declarative Syntax:")} Creating UIs by describing what they should look like, not
|
||||||
how to create them
|
how to create them
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Minimal Overhead:")} Staying close to standard Web APIs without unnecessary
|
${el("strong", "Minimal Overhead:")} Staying close to standard Web APIs without unnecessary
|
||||||
abstractions
|
abstractions
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Progressive Enhancement:")} Starting simple and adding complexity only when needed
|
${el("strong", "Progressive Enhancement:")} Starting simple and adding complexity only when needed
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Functional Composition:")} Building UIs through function composition rather than
|
${el("strong", "Functional Composition:")} Building UIs through function composition rather than
|
||||||
inheritance
|
inheritance
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Clear Patterns:")} Promoting maintainable code organization with the 3PS pattern
|
${el("strong", "Clear Patterns:")} Promoting maintainable code organization with the 3PS pattern
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Targeted Reactivity:")} Using signals for efficient, fine-grained updates
|
${el("strong", "Targeted Reactivity:")} Using signals for efficient, fine-grained updates
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Unix Philosophy:")} Doing one thing well and allowing composability with other tools
|
${el("strong", "Unix Philosophy:")} Doing one thing well and allowing composability with other tools
|
||||||
`)
|
`)
|
||||||
)
|
)
|
||||||
@ -79,7 +79,7 @@ export function page({ pkg, info }){
|
|||||||
el(h3, t`Case Studies & Real-World Applications`),
|
el(h3, t`Case Studies & Real-World Applications`),
|
||||||
|
|
||||||
el("h4", t`TodoMVC Implementation`),
|
el("h4", t`TodoMVC Implementation`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The ${el("a", references.todomvc).append("TodoMVC implementation")} showcases how dd<el> handles a complete,
|
The ${el("a", references.todomvc).append("TodoMVC implementation")} showcases how dd<el> handles a complete,
|
||||||
real-world application with all standard features of a modern web app:
|
real-world application with all standard features of a modern web app:
|
||||||
`),
|
`),
|
||||||
@ -92,40 +92,40 @@ export function page({ pkg, info }){
|
|||||||
el("li", t`Custom event system for component communication`),
|
el("li", t`Custom event system for component communication`),
|
||||||
el("li", t`Proper focus management and accessibility`)
|
el("li", t`Proper focus management and accessibility`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Key takeaways from the TodoMVC example:
|
Key takeaways from the TodoMVC example:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Signal factories like ${el("code", "routerSignal")} and ${el("code", "todosSignal")}
|
Signal factories like ${el("code", "routerSignal")} and ${el("code", "todosSignal")}
|
||||||
encapsulate related functionality
|
encapsulate related functionality
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Custom events provide clean communication between components
|
Custom events provide clean communication between components
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Targeted memoization improves rendering performance dramatically
|
Targeted memoization improves rendering performance dramatically
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Derived signals simplify complex UI logic like filtering
|
Derived signals simplify complex UI logic like filtering
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("h4", t`Migrating from Traditional Approaches`),
|
el("h4", t`Migrating from Traditional Approaches`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
When migrating from traditional DOM manipulation or other frameworks to dd<el>:
|
When migrating from traditional DOM manipulation or other frameworks to dd<el>:
|
||||||
`),
|
`),
|
||||||
el("ol").append(
|
el("ol").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Start with state:")}: Convert global variables or ad-hoc state to signals
|
${el("strong", "Start with state:")}: Convert global variables or ad-hoc state to signals
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Replace query selectors:")}: Replace getElementById/querySelector with direct references to elements
|
${el("strong", "Replace query selectors:")}: Replace getElementById/querySelector with direct references to elements
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Convert imperative updates:")}: Replace manual DOM updates with declarative signal bindings
|
${el("strong", "Convert imperative updates:")}: Replace manual DOM updates with declarative signal bindings
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Refactor into components:")}: Organize related UI elements into component functions
|
${el("strong", "Refactor into components:")}: Organize related UI elements into component functions
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
@ -211,19 +211,19 @@ export function page({ pkg, info }){
|
|||||||
el("div").append(
|
el("div").append(
|
||||||
el("h4", t`Code Organization`),
|
el("h4", t`Code Organization`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Follow the 3PS pattern:")}: Separate state creation, binding to elements, and state updates
|
${el("strong", "Follow the 3PS pattern:")}: Separate state creation, binding to elements, and state updates
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Use component functions:")}: Create reusable UI components as functions
|
${el("strong", "Use component functions:")}: Create reusable UI components as functions
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Create signal factories:")}: Extract reusable signal patterns into factory functions
|
${el("strong", "Create signal factories:")}: Extract reusable signal patterns into factory functions
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Leverage scopes:")}: Use scope for component context and clean resource management
|
${el("strong", "Leverage scopes:")}: Use scope for component context and clean resource management
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Event delegation:")}: Prefer component-level event handlers over many individual handlers
|
${el("strong", "Event delegation:")}: Prefer component-level event handlers over many individual handlers
|
||||||
`)
|
`)
|
||||||
)
|
)
|
||||||
@ -232,23 +232,23 @@ export function page({ pkg, info }){
|
|||||||
el("div").append(
|
el("div").append(
|
||||||
el("h4", t`Performance Optimization`),
|
el("h4", t`Performance Optimization`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Memoize list items:")}: Use ${el("code", "memo")} for items in frequently-updated lists
|
${el("strong", "Memoize list items:")}: Use ${el("code", "memo")} for items in frequently-updated lists
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Avoid unnecessary signal updates:")}: Only update signals when values actually change
|
${el("strong", "Avoid unnecessary signal updates:")}: Only update signals when values actually change
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Use AbortSignals:")}: Clean up resources when components are removed
|
${el("strong", "Use AbortSignals:")}: Clean up resources when components are removed
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Prefer derived signals:")}: Use computed values instead of manual updates
|
${el("strong", "Prefer derived signals:")}: Use computed values instead of manual updates
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Avoid memoizing fragments:")}: Never memoize DocumentFragments, only individual elements
|
${el("strong", "Avoid memoizing fragments:")}: Never memoize DocumentFragments, only individual elements
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
See the ${el("a", references.performance).append("Performance Optimization Guide")} for detailed strategies.
|
See the ${el("a", references.performance).append("Performance Optimization Guide")} for detailed strategies.
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
@ -324,56 +324,56 @@ export function page({ pkg, info }){
|
|||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Looking Ahead: Future Directions`),
|
el(h3, t`Looking Ahead: Future Directions`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
The dd<el> library continues to evolve, with several areas of focus for future development:
|
The dd<el> library continues to evolve, with several areas of focus for future development:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Future Compatibility:")} Alignment with the TC39 Signals proposal for native browser support
|
${el("strong", "Future Compatibility:")} Alignment with the TC39 Signals proposal for native browser support
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "SSR Improvements:")} Enhanced server-side rendering capabilities
|
${el("strong", "SSR Improvements:")} Enhanced server-side rendering capabilities
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Ecosystem Growth:")} More utilities, patterns, and integrations with other libraries
|
${el("strong", "Ecosystem Growth:")} More utilities, patterns, and integrations with other libraries
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "Documentation Expansion:")} Additional examples, tutorials, and best practices
|
${el("strong", "Documentation Expansion:")} Additional examples, tutorials, and best practices
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("strong", "TypeScript Enhancements:")} Improved type definitions and inference
|
${el("strong", "TypeScript Enhancements:")} Improved type definitions and inference
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el(h3, t`Contribution and Community`),
|
el(h3, t`Contribution and Community`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
dd<el> is an open-source project that welcomes contributions from the community:
|
dd<el> is an open-source project that welcomes contributions from the community:
|
||||||
`),
|
`),
|
||||||
el("ul").append(
|
el("ul").append(
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
${el("a", references.github).append("GitHub Repository")}: Star, fork, and contribute to the project
|
${el("a", references.github).append("GitHub Repository")}: Star, fork, and contribute to the project
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Bug reports and feature requests: Open issues on GitHub
|
Bug reports and feature requests: Open issues on GitHub
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Documentation improvements: Help expand and clarify these guides
|
Documentation improvements: Help expand and clarify these guides
|
||||||
`),
|
`),
|
||||||
el("li").append(...T`
|
el("li").append(T`
|
||||||
Examples and case studies: Share your implementations and solutions
|
Examples and case studies: Share your implementations and solutions
|
||||||
`)
|
`)
|
||||||
),
|
),
|
||||||
|
|
||||||
el("div", { className: "callout" }).append(
|
el("div", { className: "callout" }).append(
|
||||||
el("h4", t`Final Thoughts`),
|
el("h4", t`Final Thoughts`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
dd<el> provides a lightweight yet powerful approach to building modern web interfaces
|
dd<el> provides a lightweight yet powerful approach to building modern web interfaces
|
||||||
with minimal overhead and maximal flexibility. By embracing standard web technologies
|
with minimal overhead and maximal flexibility. By embracing standard web technologies
|
||||||
rather than abstracting them away, it offers a development experience that scales
|
rather than abstracting them away, it offers a development experience that scales
|
||||||
from simple interactive elements to complex applications while remaining close
|
from simple interactive elements to complex applications while remaining close
|
||||||
to what makes the web platform powerful.
|
to what makes the web platform powerful.
|
||||||
`),
|
`),
|
||||||
el("p").append(...T`
|
el("p").append(T`
|
||||||
Whether you're building a small interactive component or a full-featured application,
|
Whether you're building a small interactive component or a full-featured application,
|
||||||
dd<el>'s combination of declarative syntax, targeted reactivity, and pragmatic design
|
dd<el>'s combination of declarative syntax, targeted reactivity, and pragmatic design
|
||||||
provides the tools you need without the complexity you don't.
|
provides the tools you need without the complexity you don't.
|
||||||
|
@ -17,12 +17,14 @@
|
|||||||
*
|
*
|
||||||
* @param {TemplateStringsArray} strings
|
* @param {TemplateStringsArray} strings
|
||||||
* @param {...(string|Node)} values
|
* @param {...(string|Node)} values
|
||||||
* @returns {(string|Node)[]}
|
* @returns {DocumentFragment}
|
||||||
* */
|
* */
|
||||||
export function T(strings, ...values){
|
export function T(strings, ...values){
|
||||||
const out= [];
|
const out= [];
|
||||||
tT(s=> out.push(s), strings, ...values);
|
tT(s=> out.push(s), strings, ...values);
|
||||||
return out;
|
const fragment= document.createDocumentFragment();
|
||||||
|
fragment.append(...out);
|
||||||
|
return fragment;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Similarly to {@link T}, but for only strings.
|
* Similarly to {@link T}, but for only strings.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user