Symbiote.js:无需虚拟 DOM 的 Web 组件高级组合
Symbiote.js 是一个轻量级(~6 KB Brotli)库,用于创建 Web 组件,专注于通过 HTML 标记实现高级组合。它通过消除对虚拟 DOM 的依赖,并提供灵活的数据上下文处理机制,解决了界面结构组织的难题。与传统框架不同,Symbiote.js 直接操作 DOM API,减少样板代码,并允许通过声明式标记组合组件。
结构组合基础
界面开发包括两个方面:逻辑模型(组件、数据)和结构组合(组件连接、数据流)。Symbiote.js 为后者提供了一种独特方法,将 HTML 作为定义依赖的一等工具。主要原则:
- 组件模板是独立的 HTML 字符串
- 外部标记定义结构连接
- 不绑定 JS 运行时
- 支持混合方法(服务器/客户端渲染)
这允许你激活预生成的标记,创建无自身逻辑的“哑”组件,或使用上下文提供器。例如,使用两个关联组件实现选项卡:
import Symbiote, { css } from '@symbiotejs/symbiote';
class SuperTabs extends Symbiote {
init$ = {
'*currentTabName': 'first',
};
renderCallback() {
this.tabEls = [...this.querySelectorAll('[tab]')];
this.tabEls.forEach((/** @type {HTMLElement} */ el) => {
let tab = el.getAttribute('tab');
if (el.hasAttribute('current')) {
this.$['*currentTabName'] = tab;
}
el.onclick = () => {
this.$['*currentTabName'] = tab;
};
});
this.sub('*currentTabName', (val) => {
this.tabEls.forEach((/** @type {HTMLElement} */ el) => {
if (el.getAttribute('tab') === val) {
el.setAttribute('current', '');
} else {
el.removeAttribute('current');
}
});
});
}
}
SuperTabs.rootStyles = css`
super-tabs {
display: inline-flex;
gap: 2px;
[tab] {
cursor: pointer;
&[current] {
background-color: transparent;
pointer-events: none;
}
}
}
`;
SuperTabs.reg('super-tabs');
第二个组件负责内容显示:
class SuperTabsView extends Symbiote {
renderCallback() {
this.tabCtxEls = [...this.querySelectorAll('[tab-ctx]')];
this.sub('*currentTabName', (val) => {
this.tabCtxEls.forEach((/** @type {HTMLElement} */ el) => {
if (el.getAttribute('tab-ctx') === val) {
el.setAttribute('active', '');
} else {
el.removeAttribute('active');
}
});
});
}
}
SuperTabsView.rootStyles = css`
super-tabs-view {
display: block;
[tab-ctx] {
display: none;
&[active] {
display: contents;
}
}
}
`;
SuperTabsView.reg('super-tabs-view');
关键技术特性
示例展示了五个至关重要的机制:
- rootStyles ——无论父元素是否使用 Shadow DOM,样式都能正确应用。这确保了与 Tailwind、Bootstrap 和全局样式的兼容性。
- renderCallback ——生命周期钩子,可访问子节点(不同于标准的 connectedCallback)。
- init$ ——使用默认值初始化响应式属性。
- sub() ——通过 Pub/Sub 订阅属性变化,无需虚拟 DOM。
- *通过 (星号)声明属性** ——声明共享上下文(类似于 input[type=radio] 的 name 属性)。
架构特性是直接操作 DOM API 而无需抽象层。这不是反模式,而是有意选择,以消除虚拟表示的开销。同时,该库支持类型检查、响应性和运行时调试。
低代码范式实战
Symbiote.js 的优势在无需 JavaScript、仅通过 HTML 属性组装界面的场景中得以体现。考虑一个显示当前选项卡的组件:
class SuperCurrent extends Symbiote {}
SuperCurrent.rootStyles = css`
super-current {
display: block;
h2 {
text-transform: capitalize;
}
}
`;
SuperCurrent.template = html`<h2>{{*currentTabName}}</h2>`;
SuperCurrent.reg('super-current');
使用示例:
<super-current ctx="section-select"></super-current>
这种方法为以下场景开启了可能性:
- 设计师:直接在 HTML 中原型化界面
- 内容管理员:通过属性自定义显示,无需代码
- 嵌入式解决方案:通过标记配置小部件
- AI 系统:生成具有可预测标记和行为连接的界面
库中组件越多,通过 HTML 组装复杂界面的难度越低。每组组件通过 ctx 属性隔离,允许页面上存在多个独立实例。
核心要点
- 零打包工具依赖:组件无需 Webpack/Vite 即可工作
- 通过 DOM 实现上下文:组件间连接由标记结构定义
- 6 KB 体积:对性能影响最小
- 兼容任意技术栈:无需迁移即可集成到现有项目
- 开箱即用类型支持:无需额外配置即可支持 TypeScript
Symbiote.js 不会取代框架,而是在结构组合领域对其进行补充。对于逻辑任务,它提供极简 API,专注于其他解决方案通过第三方库实现的功能(状态管理、路由)。这在设计界面时降低了认知负担,尤其是在成员多样化的团队中。
— Editorial Team
暂无评论。