目标:零编译、零打包、纯原生 JS。基于浏览器原生 ESM + Import Map,依赖托管在内部 FMODE Repose, 业务代码直接上传 OBS + CDN,保存刷新即预览。对标新版 Angular(History 干净路由、DI、Signal 细粒度响应式、 组件/服务分层、后台多标签,无 Zone.js)。
lit、@lit-labs/* 等从 npm 下 原始 ESM 原样同步到 FMODE Repose,全业务共享。index.html、js 组件)部署到 OBS + CDN。LitElement):只渲染视图 + DOM 事件,不写复杂业务逻辑。@lit-labs/signals(细粒度信号)。@lit-labs/context(组件树注入)。utils/router.js,History 干净 URL(支持微信 OAuth 回调,需 CDN 回退规则)。
子路径部署禁用 @lit-labs/router(全局 <a> 拦截 + 未剥前缀 pathname 会污染路由),见 routing-best-practices.md。上面的「组件 / 服务 / 工具」讲的是一个应用内部怎么分。本仓还支持跨应用复用的两个方向, 二者是同一套无构建铁律在不同尺度上的应用:
┌─────────────────────────────────────────────────────────────────────┐
│ [page] 页面层 Lit 无构建页面(本应用) │
│ 组合 <my-x> 与页内组件;路由 / 多标签 / 布局只在页面层 │
│ ▲ import │
│ [wc] 组件库 Web Component(仅浏览器交互:DOM / 事件 / Shadow DOM) │
│ 一批 customElements;跨框架(Lit/Vue/React/原生)直接 <my-x> 使用 │
│ ▲ import │
│ [core] 核心库 纯 ESM 逻辑(零浏览器依赖,Node 可 import 单测) │
│ 纯函数 / 类 / 状态机;不 import Lit、不碰 window/document/fetch │
└─────────────────────────────────────────────────────────────────────┘
| 层 | 产物 | 依赖 | 规则篇 |
|---|---|---|---|
| core | npm ESM 包(可跑 Node) | 只依赖 node:* 与纯 JS 库(@lit-labs/signals 可用) |
core-library.md |
| wc | 一批自定义元素(浏览器) | core + lit(仅渲染 API) |
web-component-library.md |
| page | 一个应用的 src/(浏览器) |
wc / core + 页面 services/ utils/ |
components.md |
对应实例:fmode-code-agent = core(逻辑层纯 ESM);designer-chat 组件 = wc;
任意后台页 = page(组合 wc,并可只用页内组件而不用组件库)。
什么时候只需两层:只服务本项目、组件不复用 → 只有 page + 内联的 services/utils 就够(最常见)。 一旦出现「换一个框架还想复用这段逻辑」→ 下沉为 core;「换一个框架还想复用这个 UI」→ 上提为 wc。 别为了分层而分层,但一旦跨框架复用,core 必须零 DOM 依赖,否则复用不成立。
| 层 | 目录 | 允许 | 禁止 |
|---|---|---|---|
| 视图组件 | components/ routes/ |
渲染、DOM 事件、读 Signal、调 Service、@property 本地状态 |
大量业务逻辑、直接 fetch、硬编码接口地址、跨组件直接 new Service |
| 服务 | services/ |
持有 Signal、封装接口、校验、数据转换、业务计算 | 依赖 DOM / LitElement、import { html } 创建 DOM |
| 工具 | utils/ |
纯无状态函数、通用封装(http/storage/format) | 持有可变全局状态、依赖上面两层 |
判定口诀:能被单测、不 import Lit 的,就不是组件。服务必须能在 Node 里
import后直接跑。三分层版的口诀(见 一·五): core 不认识 DOM,wc 不认识业务,page 组装一切。 service 若连
localStorage/fetch都想抽走,就成 core; 若沾 DOM,就留在 page。
浏览器打开 URL
→ CDN 回退规则:不存在的路径 200(非 302)返回 index.html
→ index.html 加载公共 importmap(指向 FMODE Repose)
→ 加载 app.js
→ import lit 依赖
→ 初始化 路由 / Store / 根组件渲染
/npm/lit/v3.x.x/、/npm/@lit-labs/{context,signals,router}/vx/、/npm/importmap/lit-v3.json(公共 importmap)。index.html(引入 importmap)、app.js、/routes/、/components/、/services/、/utils/。⚠️ 本仓为 private。上面是内部拓扑,对外文档/开源材料中只写「内部依赖仓 + OBS+CDN」即可,不写域名、桶名、AK/SK、obsutil 路径。
| 维度 | 传统 Angular(Zone) | 本方案(Lit + Signals) |
|---|---|---|
| 变更检测 | Zone.js 全局猴子补丁,全组件树 check | Signal 精确到依赖了该信号的组件 |
| 构建 | 必须 ng build | 无构建,保存刷新即见 |
| 依赖 | 打包进 bundle | Repose 共享,一次上传全业务复用 |
| 路由 | 框架内置 | 原生 History,干净 URL,OAuth 友好 |
| 样式隔离 | ViewEncapsulation | Shadow DOM 原生隔离 |
| 心智负担 | 框架黑盒 | 原生 API,无魔法 |
<my-x>,见 web-component-library.md。