EnglishSwitch to English
打开导航

openElement 对比主流框架

一份保守的产品方向对比。本页描述每个框架的优化目标,不编造 benchmark 数据。用它判断适配度,而不是给速度排名。

适用于 v1.0.0-alpha.14 · 仓库基线 · 更新于

框架决策面

openElement — WC 原生应用框架

方面 描述
架构 Custom Elements + Declarative Shadow DOM 是一等公民;标准 Custom Elements 即应用契约;Router 掌管路由与渲染;Vite 和 Nitro 是官方构建路径。
渲染 默认 SSG;DSD/shadow 为显式选择的一等模式(当前编译默认 light DOM),按需升级元素;无需交互时输出不含任何框架 JavaScript 的纯静态内容。
DX JSX + 编译型 Custom Element 类,@element / definePage / buildApp。
适用 以 Web Components 为原生组件契约、static-first 的应用框架,用于以标准 Custom Element 契约交付 DSD-first 应用;当前范围是 static-first,而非与 Next.js、Nuxt 或 SvelteKit 的泛全栈对齐。
全栈路径 OpenElement × Supabase × Cloudflare:OpenElement 负责应用 UX,Supabase 负责数据/Auth/RLS/Storage/Realtime,Cloudflare 负责边缘/安全/缓存/异步执行。它们是服务提供方,绝不是框架内建功能。
框架 架构 渲染 DX 锁定 / 适用
Next.js — React 元框架 文件路由、React Server Components、app router、server action。 SSR / SSG / ISR、RSC 流式渲染,client component 在客户端 hydration。 React/JSX,生态庞大,在 Vercel 上是一等公民。 React 运行时加 Next.js 抽象;与 Vercel 平台亲和。
Nuxt — Vue 元框架 文件路由、Vue 单文件组件、Nitro 服务端引擎。 SSR / SSG / ISR、混合渲染、客户端 hydration。 Vue SFC、自动导入、约定驱动。 Vue 运行时加 Nuxt 与 Nitro 约定。
SvelteKit — Svelte 元框架 文件路由、Svelte 组件、Vite、基于 adapter 的部署。 SSR / SSG / CSR、渐进 hydration、无虚拟 DOM。 Svelte 编译器、语法简洁、运行时小。 Svelte 编译器/运行时;部署 adapter 可替换(锁定程度低于 Next.js)。
Astro — Islands / 内容引擎 文件路由、多框架 island、内容集合。 Static-first、island hydration、server island、View Transitions。 .astro 组件、框架无关的 island、Markdown/MDX。 低——island 可以用任何框架;有少量 Astro 特有的组件语法。
Fresh — Deno + Preact 文件路由、Preact island、Deno 原生、零构建步骤。 SSR 加 Preact island;默认客户端 JavaScript 极少。 Preact/TypeScript、Deno 运行时、无需配置 bundler。 Deno 运行时加 Preact;island 即 Preact 组件。
Lit — Web Components 基座 带响应式属性的 Custom Elements 基类;应用路由被刻意留在组件模型之外。 Lit 提供 SSR 工具链,但有服务端特有的编写约束。 TypeScript、decorator、tagged-template 渲染。 低——纯标准 Web Components;自身不带框架。
Enhance — HTML-first 的 Web Components 全栈 Custom Elements、文件路由与服务端 Custom Elements。 SSR 输出 Web Components、默认零 JS、渐进增强。 HTML-first、单文件组件、抽象极少。 低——标准 Web Components;Enhance 只加 helper,不加运行时。
Stencil — Web Components 编译器 输出标准 Web Components 的编译器;产物框架无关。 客户端 Web Components,带预渲染、懒加载和内部虚拟 DOM。 TSX、decorator、面向设计系统的工具链。 产物是无锁定的 Web Components;编写时使用 Stencil 工具链。
FAST / Web Awesome — 组件体系 FAST 提供 Web Component 编写基础设施;Web Awesome 分发组件库与设计资产。 — — 当你的首要需求是组件体系时任选其一。openElement 并不替代成熟的设计系统,应作为围绕组件的应用框架来评估。

如何阅读本页

  • 架构——路由、组件与服务端如何组合。
  • 渲染——SSR/SSG/CSR 默认值、hydration 与 island 策略。
  • DX——语言、工具链与学习曲线。
  • 锁定——你与专有运行时或平台的绑定程度,对照开放标准。

三组框架,三个不同的问题

  • Lit / FAST / Stencil 是组件层,而不是同一个应用契约。它们编写或编译 Custom Elements,并刻意把路由、数据与应用闭环留在自身模型之外;openElement 在同一个标准之上构建应用契约,因此它们与之组合,而非竞争。
  • Astro / Fresh / Enhance 是 static-first 或 HTML-first 的基线,但持久组件模型不同——框架专有的组件格式或绑定框架的 island。在 openElement 中,持久模型就是标准 Custom Element 本身,DSD 是显式声明的 shadow 模式的服务端表达(当前编译默认是 light DOM)。
  • Next / Remix / Nuxt / SvelteKit 是更宽泛的、框架专有的全栈生态。openElement 不宣称与它们的泛全栈对等;它的全栈故事是与外部服务提供方显式、有证据支撑的组合。

决策标准

  • 选择 openElement 当 Web Components 是对外集成面,且 SSR 输出需要保留浏览器原生的组件边界时。
  • 选择 Astro / Enhance / Lit / Stencil 当标准优先的 Web Components 方案很重要,且想避开沉重的应用运行时时。
  • 选择 Next.js / Nuxt / SvelteKit 当你的产品明确围绕 React、Vue 或 Svelte 应用模型构建时。
  • 选择 Fresh 当你想要 Deno 原生、近乎零构建的 Preact island 体验时。
  • 不要选择 openElement 当主要诉求是成熟生态、框架专属 UI 运行时或现成的企业级设计系统时。评估 openElement 的团队仍应在自己的生产环境中验证 SaaS 消费方与部署路径。

官方组合路径

OpenElement × Supabase × Cloudflare 是第一方全栈交付路径,所有权边界明确:OpenElement 负责应用 UX;Supabase 负责数据、Auth、RLS、Storage 与 Realtime;Cloudflare 负责边缘交付、安全、缓存与异步执行。Supabase 与 Cloudflare 是被组合的服务提供方——绝不是框架内建功能——包图谱边界门禁保证服务提供方代码不进入框架包。

最初随 0.43 线与 Universal WC SSR 一同交付,并由当前编译型版本线继承。框架自有的生产运行时恢复与缓存语义仍在当前契约之外,尚未分配发布版本。

定位背后的证据

  • Custom Elements 作为持久的应用契约——静态面与请求时应用闭环由当前架构文档描述。当前架构
  • DSD-first SSR 与选择性升级,以及显式的外来 WC 准入——语料库把每个第三方库形态的观测 SSR 输出与准入钉为机器可读证据。第三方 WC 互操作语料库
  • 浏览器与打包产物验证——候选版本需要 Chromium、Firefox 与 WebKit 证明,消费方从打包的公开产物构建。发布流程
  • 可组合的服务提供方栈,而不是框架自有的 Auth 或数据库包——由第一方 SaaS 消费方实现并通过单元测试;托管与部署的 qualification 仍待外部完成,消费方 README 记录着该 qualification 状态。Supabase × Cloudflare SaaS

来源与评审范围

2026-08-16 依据各项目一手文档评审。这是一份决策指南,不是基准测试,也不是兼容性认证。

实测输出

我们测量什么,以及复现每一行的命令。

产物体积

以下数字于 2026-10-07 量自 docs 站点自身的构建(www/dist,由 pnpm run site:build 生成)。下列命令可复现每一行;页面数与 URL 数随路由集合变化,内容变更后请重跑。alpha6 之后数字变动过五次:alpha6 构建把客户端资产注入改为 manifest 驱动、包 island 采用精确身份匹配(#1471、ADR-0160);alpha8 的 B2 manifest 转换(pnpm workspaces,不再有 deno.json workspace 标记)重新切分了应用侧 island chunk;alpha8 的客户端构建现在会在 chunk 分组前把每个包 island 声明的身份解析到真实模块 id(#1471 fail-closed);alpha9 的样式列车让站点落座 @theme 角色表并接入 Zag 驱动的 open-search 岛(#1502),带动了下方 island chunk 与路由载荷的增长,因此每个 island chunk 都以 island-open-<tag>-<hash>.js 命名,而共享的编译元素运行时单独成块、由 client.js 在每个 island 页面上即时加载;alpha10 列车把 island 组件样式做了压缩(#1543),让共享的编译元素运行时从寄居的 island chunk 挪进独立的 element-runtime-<hash>.js chunk(#1544),再把 island 样式表整体从 chunk 中抽出、成为按内容寻址的 client/assets/*.css 资源(#1553、ADR-0164)。alpha.11 的样式列车(#1558、ADR-0164)随后把包 island 与站点页面剩余的内联样式表迁到同一 .css import 形态,下方 island 行与路由载荷随之再次重切。下方的路由载荷按路由 manifest chunk 集合的即时导入闭包测量,因此无论共享代码落在哪个 chunk 文件里,数字都保持诚实。

指标 数值
预渲染文档 72 个 HTML 文件
sitemap.xml URL 数 70
静态产物总量 8.6 MB
island manifest 72 份——每页一份
搜索索引 每个语言 35 页(en、zh),70 个 fragment,1.3 MB
pnpm run site:build                         # 先重新生成以下全部内容
find www/dist -name '*.html' | wc -l        # 72
grep -c '<loc>' www/dist/sitemap.xml        # 70
du -sh www/dist                             # 9.3M(随平台变化;上表 8.6 MB 是字节总和)
ls www/dist/island-manifests | wc -l        # 72
cat www/dist/pagefind/pagefind-entry.json   # 每种语言 page_count 35

Island bundle

docs 站点就是一个普通的 openElement 应用(同样有 island),所以它的客户端产物是有代表性的样本。以下是全部 island chunk 与共享入口/运行时 chunk 的原始体积与 gzip 体积:

Chunk 原始字节 gzip -9
element-runtime 83,172 26,038
island-open-button 13,323 2,398
island-open-dragon-live-gaze 10,988 4,063
island-open-page-rail 9,461 2,863
island-open-cinematic-scroll 7,013 2,809
island-open-code-block 3,323 1,229
island-open-hero-polish 5,359 2,254
client.js 6,293 1,919
ls -l www/dist/client/islands/*.js
gzip -9 -c www/dist/client/islands/client.js | wc -c

island 入口(client.js)承载 island import 工厂与错误串,字节跟随被准入的 island 集合;自 #1367 修复把模块 id 锚定到 workspace 段之后,它不再内嵌检出绝对路径,因此与其他行一样钉死数字。自 alpha10 的 chunk 重新分组(#1544)起,共享的编译元素运行时住进自己的 element-runtime-<hash>.js chunk,不再寄居在某个 island chunk 里——client.js 与每个 island chunk 都静态导入它,所以无论路由是什么,这份字节都会随每个 island 页面一起下载。另有两类产出不进表格:约 99 KB 的搜索 combobox 运行时是首次打开搜索时才拉取的动态 chunk;组件样式表则根本不是 island JS——它们按 island 样式资产协议(#1553、ADR-0164)产出为按内容寻址的 client/assets/*.css 资源并在浏览器中采用,这正是上方 island 行相比上一基线大幅下降的原因。同一类跨平台字节方差下,逐 chunk 的原始字节行容忍 ±1%、gzip 行容忍 ±3%;下面的路由载荷容忍 ±1%;chunk 数量保持精确。

页面实际下载什么,由它自己的 island manifest 加上入口的即时(静态)导入决定——载荷行按该导入闭包测量,不随共享代码所在的 chunk 文件变化:

路由 客户端载荷(原始) 不同 chunk 数
/guide/mdx 150,163 B 8
/guide/getting-started 150,163 B 8
/ 166,510 B 10

72 份页面 manifest 合计声明了 9 个 island 标签、342 条记录:外壳 island(open-layout、open-search、open-theme-toggle)出现在每一页,open-page-rail 出现在 62 页,open-code-block 出现在 50 页,其余标签只在少数页面上。#1557 的 B8 退役移除了 open-badge 包 island(roadmap 上的用法已改为纯 HTML+CSS recipe),而参考页新增了两个拆分包的文档,使载荷行发生移动。

cat www/dist/island-manifests/page-<hash>.json   # 单页的 island 集合:标签、chunk、策略、层级
python3 -c "import json,glob,collections; print(collections.Counter(t for f in glob.glob('www/dist/island-manifests/*.json') for t in (i['tagName'] for i in json.load(open(f))['islands'])))"

没有 island、也没有增强表单的项目完全不产出客户端入口:DSD 组件不需要框架的虚拟 DOM 运行时,因此纯静态页面保持无脚本。

渲染

指标 行为
DSD SSR 组件序列化为 Declarative Shadow DOM;浏览器原生解析 shadow root,没有脚本解析成本。
Island 水合 按组件粒度,由其声明的策略门控(load / idle / visible / only)。
导航 浏览器原生导航;View Transitions 与 Speculation Rules 均为可选。
强制运行时 没有。客户端 JavaScript 只存在于模块声明了 island、或表单选择增强的地方。

另见

  • 当前架构——这份对比所引用的包依赖图,以及它准入的 package 兼容契约。
  • 核心概念——这一立场背后的创作模型。
  • Island Hydration——四层组件模型及其策略。
  • Design System——本站样式与 token 的构成。