mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-04-01 19:55:53 +02:00
🔤 ssr
This commit is contained in:
parent
4edc509646
commit
b08f75bfb0
43
docs/components/examples/ssr/async-data.js
Normal file
43
docs/components/examples/ssr/async-data.js
Normal file
@ -0,0 +1,43 @@
|
||||
// Handling async data in SSR
|
||||
import { JSDOM } from "jsdom";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
import { register, queue } from "deka-dom-el/jsdom";
|
||||
|
||||
async function renderWithAsyncData() {
|
||||
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>");
|
||||
const { el } = await register(dom);
|
||||
|
||||
// Create a component that fetches data
|
||||
function AsyncComponent() {
|
||||
const title= S("-");
|
||||
const description= S("-");
|
||||
|
||||
// Use the queue to track the async operation
|
||||
queue(fetch("https://api.example.com/data")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
title.set(data.title);
|
||||
description.set(data.description);
|
||||
}));
|
||||
|
||||
return el("div", { className: "async-content" }).append(
|
||||
el("h2", title),
|
||||
el("p", description)
|
||||
);
|
||||
}
|
||||
|
||||
// Render the page
|
||||
dom.window.document.body.append(
|
||||
el("h1", "Page with Async Data"),
|
||||
el(AsyncComponent)
|
||||
);
|
||||
|
||||
// IMPORTANT: Wait for all queued operations to complete
|
||||
await queue();
|
||||
|
||||
// Now the HTML includes all async content
|
||||
const html = dom.serialize();
|
||||
console.log(html);
|
||||
}
|
||||
|
||||
renderWithAsyncData();
|
47
docs/components/examples/ssr/basic-example.js
Normal file
47
docs/components/examples/ssr/basic-example.js
Normal file
@ -0,0 +1,47 @@
|
||||
// Basic SSR Example
|
||||
import { JSDOM } from "jsdom";
|
||||
import { register, queue } from "deka-dom-el/jsdom";
|
||||
import { writeFileSync } from "node:fs";
|
||||
|
||||
async function renderPage() {
|
||||
// Create a jsdom instance
|
||||
const dom = new JSDOM("<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head><body></body></html>");
|
||||
|
||||
// Register with deka-dom-el and get the el function
|
||||
const { el } = await register(dom);
|
||||
|
||||
// Create a simple header component
|
||||
function Header({ title }) {
|
||||
return el("header").append(
|
||||
el("h1", title),
|
||||
el("nav").append(
|
||||
el("ul").append(
|
||||
el("li").append(el("a", { href: "/" }, "Home")),
|
||||
el("li").append(el("a", { href: "/about" }, "About")),
|
||||
el("li").append(el("a", { href: "/contact" }, "Contact"))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Create the page content
|
||||
dom.window.document.body.append(
|
||||
el(Header, { title: "My Static Site" }),
|
||||
el("main").append(
|
||||
el("h2", "Welcome!"),
|
||||
el("p", "This page was rendered with deka-dom-el on the server.")
|
||||
),
|
||||
el("footer", "© 2025 My Company")
|
||||
);
|
||||
|
||||
// Wait for any async operations
|
||||
await queue();
|
||||
|
||||
// Get the HTML and write it to a file
|
||||
const html = dom.serialize();
|
||||
writeFileSync("index.html", html);
|
||||
|
||||
console.log("Page rendered successfully!");
|
||||
}
|
||||
|
||||
renderPage().catch(console.error);
|
2
docs/components/examples/ssr/intro.js
Normal file
2
docs/components/examples/ssr/intro.js
Normal file
@ -0,0 +1,2 @@
|
||||
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js
|
||||
import { register, unregister, queue } from "deka-dom-el/jsdom";
|
35
docs/components/examples/ssr/pages.js
Normal file
35
docs/components/examples/ssr/pages.js
Normal file
@ -0,0 +1,35 @@
|
||||
// ❌ WRONG: Static imports are hoisted and will register before JSDOM is created
|
||||
import { register } from "deka-dom-el/jsdom";
|
||||
import { el } from "deka-dom-el";
|
||||
import { Header } from "./components/Header.js";
|
||||
|
||||
// ✅ CORRECT: Use dynamic imports to ensure proper initialization order
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
async function renderPage() {
|
||||
// 1. Create JSDOM instance first
|
||||
const dom = new JSDOM(`<!DOCTYPE html><html><body></body></html>`);
|
||||
|
||||
// 2. Dynamically import jsdom module
|
||||
const { register, queue } = await import("deka-dom-el/jsdom");
|
||||
|
||||
// 3. Register and get el function
|
||||
const { el } = await register(dom);
|
||||
|
||||
// 4. Dynamically import page components
|
||||
const { Header } = await import("./components/Header.js");
|
||||
const { Content } = await import("./components/Content.js");
|
||||
|
||||
// 5. Render components
|
||||
const body = dom.window.document.body;
|
||||
el(body).append(
|
||||
el(Header, { title: "My Page" }),
|
||||
el(Content, { text: "This is server-rendered content" })
|
||||
);
|
||||
|
||||
// 6. Wait for async operations
|
||||
await queue();
|
||||
|
||||
// 7. Get HTML and clean up
|
||||
return dom.serialize();
|
||||
}
|
27
docs/components/examples/ssr/start.js
Normal file
27
docs/components/examples/ssr/start.js
Normal file
@ -0,0 +1,27 @@
|
||||
// Basic jsdom integration example
|
||||
import { JSDOM } from "jsdom";
|
||||
import { register, unregister, queue } from "deka-dom-el/jsdom.js";
|
||||
|
||||
// Create a jsdom instance
|
||||
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>");
|
||||
|
||||
// Register the dom with deka-dom-el
|
||||
const { el } = await register(dom);
|
||||
|
||||
// Use deka-dom-el normally
|
||||
dom.window.document.body.append(
|
||||
el("div", { className: "container" }).append(
|
||||
el("h1", "Hello, SSR World!"),
|
||||
el("p", "This content was rendered on the server.")
|
||||
)
|
||||
);
|
||||
|
||||
// Wait for any async operations to complete
|
||||
await queue();
|
||||
|
||||
// Get the rendered HTML
|
||||
const html = dom.serialize();
|
||||
console.log(html);
|
||||
|
||||
// Clean up when done
|
||||
unregister();
|
44
docs/components/examples/ssr/static-site-generator.js
Normal file
44
docs/components/examples/ssr/static-site-generator.js
Normal file
@ -0,0 +1,44 @@
|
||||
// Building a simple static site generator
|
||||
import { JSDOM } from "jsdom";
|
||||
import { register, queue } from "deka-dom-el/jsdom";
|
||||
import { writeFileSync, mkdirSync } from "node:fs";
|
||||
|
||||
async function buildSite() {
|
||||
// Define pages to build
|
||||
const pages = [
|
||||
{ id: "index", title: "Home", component: "./pages/home.js" },
|
||||
{ id: "about", title: "About", component: "./pages/about.js" },
|
||||
{ id: "docs", title: "Documentation", component: "./pages/docs.js" }
|
||||
];
|
||||
|
||||
// Create output directory
|
||||
mkdirSync("./dist", { recursive: true });
|
||||
|
||||
// Build each page
|
||||
for (const page of pages) {
|
||||
// Create a fresh jsdom instance for each page
|
||||
const dom = new JSDOM("<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head><body></body></html>");
|
||||
|
||||
// Register with deka-dom-el
|
||||
const { el } = await register(dom);
|
||||
|
||||
// Import the page component
|
||||
const { default: PageComponent } = await import(page.component);
|
||||
|
||||
// Render the page with its metadata
|
||||
dom.window.document.body.append(
|
||||
el(PageComponent, { title: page.title, pages })
|
||||
);
|
||||
|
||||
// Wait for any async operations
|
||||
await queue();
|
||||
|
||||
// Write the HTML to a file
|
||||
const html = dom.serialize();
|
||||
writeFileSync(`./dist/${page.id}.html`, html);
|
||||
|
||||
console.log(`Built page: ${page.id}.html`);
|
||||
}
|
||||
}
|
||||
|
||||
buildSite().catch(console.error);
|
129
docs/p08-ssr.html.js
Normal file
129
docs/p08-ssr.html.js
Normal file
@ -0,0 +1,129 @@
|
||||
import { T, t } from "./utils/index.js";
|
||||
export const info= {
|
||||
title: t`Server-Side Rendering (SSR)`,
|
||||
description: t`Using deka-dom-el for server-side rendering with jsdom to generate static HTML.`,
|
||||
};
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
import { simplePage } from "./layout/simplePage.html.js";
|
||||
import { h3 } from "./components/pageUtils.html.js";
|
||||
import { code } from "./components/code.html.js";
|
||||
/** @param {string} url */
|
||||
const fileURL= url=> new URL(url, import.meta.url);
|
||||
|
||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
|
||||
export function page({ pkg, info }){
|
||||
const page_id= info.id;
|
||||
return el(simplePage, { info, pkg }).append(
|
||||
el("h2", t`Server-Side Rendering with deka-dom-el`),
|
||||
el("p").append(...T`
|
||||
deka-dom-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.
|
||||
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.
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/ssr/intro.js"), page_id }),
|
||||
|
||||
el(h3, t`Why Server-Side Rendering?`),
|
||||
el("p").append(...T`
|
||||
SSR offers several benefits:
|
||||
`),
|
||||
el("ul").append(
|
||||
el("li", t`Improved SEO - Search engines can easily index fully rendered content`),
|
||||
el("li", t`Faster initial page load - Users see content immediately without waiting for JavaScript to load`),
|
||||
el("li", t`Better performance on low-powered devices - Less JavaScript processing on the client`),
|
||||
el("li", t`Content available without JavaScript - Useful for users who have disabled JavaScript`),
|
||||
el("li", t`Static site generation - Build files once, serve them many times`)
|
||||
),
|
||||
|
||||
el(h3, t`How jsdom Integration Works`),
|
||||
el("p").append(...T`
|
||||
The jsdom export in deka-dom-el provides the necessary tools to use the library in Node.js
|
||||
by integrating with jsdom. Here's what it does:
|
||||
`),
|
||||
el("ol").append(
|
||||
el("li", t`Creates a virtual DOM environment in Node.js using jsdom`),
|
||||
el("li", t`Registers DOM globals like HTMLElement, document, etc. for deka-dom-el to use`),
|
||||
el("li", t`Sets an SSR flag in the environment to enable SSR-specific behaviors`),
|
||||
el("li", t`Provides a promise queue system for managing async operations during rendering`),
|
||||
el("li", t`Handles DOM property/attribute mapping differences between browsers and jsdom`)
|
||||
),
|
||||
el(code, { src: fileURL("./components/examples/ssr/start.js"), page_id }),
|
||||
|
||||
el(h3, t`Basic SSR Example`),
|
||||
el("p").append(...T`
|
||||
Here's a simple example of how to use deka-dom-el for server-side rendering in a Node.js script:
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/ssr/basic-example.js"), page_id }),
|
||||
|
||||
el(h3, t`Building a Static Site Generator`),
|
||||
el("p").append(...T`
|
||||
You can build a complete static site generator with deka-dom-el. In fact, this documentation site
|
||||
is built using deka-dom-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(h3, t`Working with Async Content in SSR`),
|
||||
el("p").append(...T`
|
||||
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.
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/ssr/async-data.js"), page_id }),
|
||||
|
||||
el(h3, t`Working with Dynamic Imports for SSR`),
|
||||
el("p").append(...T`
|
||||
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.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
Why is this important?
|
||||
`),
|
||||
el("ul").append(
|
||||
el("li").append(...T`
|
||||
${el("strong", "Static imports are hoisted:")} JavaScript hoists import statements to the top of the file,
|
||||
executing them before any other code
|
||||
`),
|
||||
el("li").append(...T`
|
||||
${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
|
||||
${el("em", "before")} you import your components using ${el("code", "import { el } from \"deka-dom-el\";")}.
|
||||
`),
|
||||
el("li").append(...T`
|
||||
${el("strong", "Correct initialization order:")} You need to control the exact sequence of:
|
||||
create JSDOM → register environment → import components
|
||||
`)
|
||||
),
|
||||
el("p").append(...T`
|
||||
Follow this pattern when creating server-side rendered pages:
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/ssr/pages.js"), page_id }),
|
||||
|
||||
el(h3, t`SSR Considerations and Limitations`),
|
||||
el("p").append(...T`
|
||||
When using deka-dom-el for SSR, keep these considerations in mind:
|
||||
`),
|
||||
el("ul").append(
|
||||
el("li", t`Browser-specific APIs like window.localStorage are not available in jsdom by default`),
|
||||
el("li", t`Event listeners added during SSR won't be functional in the final HTML unless hydrated on the client`),
|
||||
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("p").append(...T`
|
||||
For advanced SSR applications, consider implementing hydration on the client-side to restore
|
||||
interactivity after the initial render.
|
||||
`),
|
||||
|
||||
el(h3, t`Real Example: How This Documentation is Built`),
|
||||
el("p").append(...T`
|
||||
This documentation site itself is built using deka-dom-el's SSR capabilities.
|
||||
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("p").append(...T`
|
||||
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
|
||||
to render the initial content.
|
||||
`),
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user