# 复盘:纯原生 ESM × Lit 3.x,对标 Angular zoneless > 本文记录技术选型的**动机、取舍与现代对标**,作为规则背后的「为什么」。 > 结论先行:**在内部中后台场景,这套无构建方案以更少的心智负担,达成了 Angular zoneless 想达成的目标。** ## 一、我们想要什么(对标面) 新版 Angular 的几个现代卖点,正是内部后台长期缺的东西: | Angular 现代能力 | 我们要的等价物 | |------------------|----------------| | **zoneless(无 Zone.js)** | 没有全局猴子补丁,更新精确、可预测 | | **Signals** | 细粒度响应式,改一个值只重渲依赖它的地方 | | **DI(`inject`)** | 依赖注入,组件树拿到服务 | | **Standalone 组件** | 组件即自定义元素,随处 import 即用 | | **干净路由(无 hash)** | History 路由,OAuth 友好 | | **组件样式隔离** | Shadow DOM 原生隔离 | ## 二、Angular vs 本方案:逐项对照 | 维度 | Angular(Zone + Signals) | Lit 原生 ESM + Signals | |------|---------------------------|------------------------| | 构建 | 必须 `ng build`(编译 TS/模板) | **无构建**,源码即产物 | | 变更检测 | Zone.js 打补丁 → 框架调度;zoneless 后靠 Signal 图 | **无 Zone**,Signal 直接驱动订阅者 | | 依赖分发 | 打包进 bundle | Repose 统一托管,一次上传全业务复用 | | 组件模型 | 装饰器 + 模板 DSL(框架私有) | **标准 Web Components** + 标准模板字面量 | | 样式隔离 | ViewEncapsulation(模拟/Shadow) | **Shadow DOM 原生** | | 路由 | `@angular/router` | 自写 `utils/router.js`(History;子路径部署弃用 `@lit-labs/router`) | | DI | 框架 DI 容器 | `@lit-labs/context`(轻)或直接 import 单例 | | 心智模型 | 框架概念多(模块/组件/指令/管道…) | **浏览器原生 API**,概念少 | | 升级成本 | 框架版本强绑定 | 只升级 lit 版本号(改 importmap) | | 产物透明度 | 打包黑盒 | **无黑盒**,模块即文件 | ## 三、为什么「无 Zone」在这里成立 Angular 需要 Zone.js 是因为它要**拦截所有异步 API**(setTimeout/Promise/XHR/事件), 在异步完成后触发全局变更检测——代价是全局猴子补丁 + 大范围 check。 本方案的响应式来源是**显式的 Signal**: - 状态放进 `signal()`;组件读取时订阅;`.set()` 时精确通知**读过的**组件。 - 没有隐式依赖跟踪全局,没有「任何异步都触发全树 check」。 - 副作用用 `effect()` 显式声明。 结论:**Signal 本来就是 zoneless 的前提,Lit + signals 是天然的 zoneless 组合**。 ## 四、取舍与代价(诚实的部分) | 得到 | 代价 | |------|------| | 无构建、保存刷新即见 | 失去 tree-shaking / minify / 代码分割(靠 HTTP/2 + importmap + 缓存缓解) | | 依赖不打包 | 浏览器发起的模块请求更多(HTTP/2 多路复用) | | 标准 Web Components | 无框架级 TS 模板类型检查(本地 `npm i` 仅为提示) | | 概念少 | 需要团队懂原生 ESM / 浏览器语义(如 CORS、MIME、History) | | Shadow DOM 隔离 | 全局主题需用 CSS 自定义属性穿透 | **适用边界**:内部中后台、组件复用、快速迭代——预算充足。重 SEO / SSR 的站点仍应上框架。 ## 五、与「传统打包 SPA」的实际收益对比 | 环节 | 传统打包 | 本方案 | |------|----------|--------| | 改一行代码到看见效果 | 触发 rebuild(秒~分钟级) | **刷新浏览器** | | 依赖升级 | 重新 build 全量 | 改 importmap 版本号 | | 多项目共享组件 | 各自打包发 npm | Repose 共享,改一处全生效 | | 调试 | sourcemap 还原 | **源码即运行的代码**,无映射 | | 部署 | 上传 dist | 上传 `src/`(无构建产物) | ## 六、可迁移到其他场景的判断表 | 你的项目 | 建议 | |----------|------| | 内部工具 / 运营后台 / 数据看板 | ✅ 本方案 | | 组件库 / 设计系统 | ✅ 本方案(Web Components 天然跨栈复用) | | 活动页 / 嵌入页(体积敏感、迭代快) | ✅ 本方案 | | 内容站 / 电商前台(SEO 首屏) | ❌ 用带 SSR 的方案(Astro 等) | | 超大型多团队应用,强 TS 类型约束 | ⚠️ 可用,但需团队接受「无构建期类型检查」的约束 | ## 七、经验总结 1. **构建的收益主要在「优化」,不在「能用」**。当网络条件(HTTP/2 + CDN)与缓存补齐后,无构建对内部后台是净收益。 2. **Signal 是响应式的现代答案**,它让「无 Zone」不是妥协而是更优——Angular 自己也在往这走。 3. **标准 > 框架私有**:Web Components + 原生 ESM 的产出,十年后仍能跑;框架 DSL 迭代则需跟着升级。 4. **分层是纪律问题,不是技术问题**:无框架约束时,靠规则(本仓)与评审守住「组件只渲染、服务管业务」。 5. **部署细节是最大的坑**:CORS / MIME / History 回退 / base href——这些与代码无关,却决定成败,务必进 checklist。