LessJS 的 island 是 DSD HTML 之后的 Custom Element upgrade。它不是整页 hydration, 也不是把应用状态完整恢复到客户端。
浏览器解析 HTML 时,Declarative Shadow DOM 已经把组件内容和样式放进 shadow root。 客户端入口加载后调用 customElements.define(), 浏览器把已有元素升级为真正的 Custom Element。这个过程更准确地叫 Island Upgrade。
| Need | Preferred layer |
|---|---|
| Readable content, navigation, layout | HTML + DSD |
| Hover, focus, responsive state, simple disclosure | CSS and native HTML elements |
| Clipboard, localStorage, IntersectionObserver, BroadcastChannel | Small island using browser APIs |
| Local state, event orchestration, API polling, optimistic UI | Island Upgrade |
本地 island 放在 app/islands。构建器会扫描它, 生成 client entry,并在静态 HTML 中注入入口脚本。
// app/islands/my-counter.ts
import { css, html, LitElement } from 'lit';
export const tagName = 'my-counter';
export default class MyCounter extends LitElement {
static override styles = css`
:host { display: inline-flex; gap: 0.5rem; align-items: center; }
`;
count = 0;
override render() {
return html`
<button @click=\() => this.count-->-</button>
<span>\</span>
<button @click=\() => this.count++>+</button>
`;
}
}
customElements.define(tagName, MyCounter);
<my-counter></my-counter>
首屏 HTML 中会出现 host 和 shadow root。浏览器加载 my-counter 模块后,按钮事件才会开始工作。
可复用包可以导出 island metadata,LessJS 在构建时读取这些信息,用于 SSR 注册和客户端入口生成。
import type { PackageIslandMeta } from '@lessjs/core';
export const islands: PackageIslandMeta[] = [
{
tagName: 'less-theme-toggle',
modulePath: '@lessjs/ui/less-theme-toggle',
strategy: 'eager',
},
];
当前实现仍以全局 island entry 为主。下一阶段需要把 strategy 从 metadata 真正带进 client build, 并引入页面级 island manifest,让每个页面只加载实际出现的 island。