本文记录技术选型的动机、取舍与现代对标,作为规则背后的「为什么」。 结论先行:在内部中后台场景,这套无构建方案以更少的心智负担,达成了 Angular zoneless 想达成的目标。
新版 Angular 的几个现代卖点,正是内部后台长期缺的东西:
| Angular 现代能力 | 我们要的等价物 |
|---|---|
| zoneless(无 Zone.js) | 没有全局猴子补丁,更新精确、可预测 |
| Signals | 细粒度响应式,改一个值只重渲依赖它的地方 |
DI(inject) |
依赖注入,组件树拿到服务 |
| Standalone 组件 | 组件即自定义元素,随处 import 即用 |
| 干净路由(无 hash) | History 路由,OAuth 友好 |
| 组件样式隔离 | Shadow DOM 原生隔离 |
| 维度 | 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) |
| 产物透明度 | 打包黑盒 | 无黑盒,模块即文件 |
Angular 需要 Zone.js 是因为它要拦截所有异步 API(setTimeout/Promise/XHR/事件), 在异步完成后触发全局变更检测——代价是全局猴子补丁 + 大范围 check。
本方案的响应式来源是显式的 Signal:
signal();组件读取时订阅;.set() 时精确通知读过的组件。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 的站点仍应上框架。
| 环节 | 传统打包 | 本方案 |
|---|---|---|
| 改一行代码到看见效果 | 触发 rebuild(秒~分钟级) | 刷新浏览器 |
| 依赖升级 | 重新 build 全量 | 改 importmap 版本号 |
| 多项目共享组件 | 各自打包发 npm | Repose 共享,改一处全生效 |
| 调试 | sourcemap 还原 | 源码即运行的代码,无映射 |
| 部署 | 上传 dist | 上传 src/(无构建产物) |
| 你的项目 | 建议 |
|---|---|
| 内部工具 / 运营后台 / 数据看板 | ✅ 本方案 |
| 组件库 / 设计系统 | ✅ 本方案(Web Components 天然跨栈复用) |
| 活动页 / 嵌入页(体积敏感、迭代快) | ✅ 本方案 |
| 内容站 / 电商前台(SEO 首屏) | ❌ 用带 SSR 的方案(Astro 等) |
| 超大型多团队应用,强 TS 类型约束 | ⚠️ 可用,但需团队接受「无构建期类型检查」的约束 |