3.1 整体架构与组件集成


3.1 整体架构与组件集成

本节摘要:PWA 不是松散能力的拼贴画,而是外壳、内核、数据三个功能自治、契约清晰的区域。外壳保证零网速下三百毫秒内出框架,内核负责能力探测与动态调度,数据层用变更日志加同步协调器实现离线优先。本节讲清三层的边界、通信方式与协同机制。(素材映射:原 3.1 节全部内容。)

上手前先明确

阅读完本节,你应当能够:

  1. 说明外壳-内核-数据三层各自的"不可妥协指标";
  2. 设计一条从本地写入到后台回传的完整同步链路;
  3. 用事件溯源思路组织前端数据流;
  4. 搭建"探测-声明-降级"三层能力契约。

一、为什么要分层:集市与城市

传统单页应用常像一座未经规划的集市——摊位(组件)随意摆放,供水(数据)、供电(状态)、交通(路由)管线彼此缠绕,一处水管爆裂整条街瘫痪。PWA 的架构哲学首先是一次空间秩序的重构:把城市划分为外壳、内核、数据三个区域。这不是教条分层,而是对 Web 运行时三对矛盾的回应:界面渲染必须快于人类视觉暂留(约十六毫秒一帧),状态同步必须容忍网络不可靠,数据持久化必须跨越进程生命周期。

外壳层是地基加穹顶。它不承载业务逻辑,却决定用户第一眼所见:初始页面骨架、内联关键样式、字体预加载、轻量路由占位,以及一个永不卸载的应用壳容器。这个容器由静态资源构成、被 Service Worker 严格缓存,确保哪怕在零网速下,用户点图标后也能在三百毫秒内看到结构完整的界面框架。它不关心今天订单有多少,只承诺按钮在左、导航在上、加载动画有明确位置。这种"确定性交付"正是 PWA 区别于传统 Web 的分水岭——把"首屏可交互时间"从一个统计均值,压缩为一个可工程化保障的硬性指标。

内核层是市政中枢。包含路由解析、状态管理、组件协调、意图识别,以及最关键的能力探测与降级调度器。这个调度器不预设浏览器能力,而是运行时实时探测各接口的可用性并动态加载适配层:某浏览器不支持后台获取但支持周期同步,调度器就绕过前者启用后者;检测到服务端已配好导航预加载头,就自动激活预加载管道。这使内核天然具备跨浏览器韧性,而非依赖转译或垫片的被动兼容。

数据层是地下管网与中央水库,由三部分构成:瞬时缓存基于缓存接口,服务于请求拦截,遵循 HTTP 语义;持久实体库基于本地数据库,用领域建模思路为每个实体配独立的对象仓库、版本迁移脚本与冲突解决策略;同步协调器监听同步事件,把本地变更队列按业务语义分组,绑定网络恢复条件,实现语义化、幂等、可中断恢复的数据同步。

图:三层架构职责与数据流

图:三层架构职责与数据流

二、组件协同:一条消息驱动的链路

架构的优雅最终落于组件间如何呼吸吐纳。一切协同的起点,是 Service Worker 作为中央神经节——它拦截请求、进缓存、做裁决:

self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); if (url.pathname.startsWith('/api/')) { event.respondWith( caches.match(event.request).then(cached => { if (cached) return cached; // 快路径 return fetch(event.request).then(res => { const copy = res.clone(); event.waitUntil( caches.open('api-cache').then(c => c.put(event.request, copy)) // 按策略写缓存 ); return res; // 慢路径 }); }) ); } });

这段代码揭示的是双轨制响应机制:不强迫用户等网络,也不盲目给陈旧数据,在"可用即用"与"新鲜即真"之间动态平衡。

页面与 Worker 的对话通过消息信道建立,流动的不是原始字节而是携带语义的领域消息:

// 页面发出 navigator.serviceWorker.controller?.postMessage({ type: 'SYNC_CART', payload: { items: cartItems, ts: Date.now() } }); // Worker 接收并回执 self.addEventListener('message', (event) => { if (event.data.type === 'SYNC_CART') { writeToLocalDB('carts', event.data.payload).then(() => { event.source.postMessage({ type: 'SYNC_SUCCESS' }); }); } });

注意两个细节:回执方法保证消息精准回到发起者而非广播风暴;载荷携带完整业务上下文而非标识引用。页面无需轮询无需长连接,一次事件监听就获得最终一致性保证。

更深层的协同发生在状态管理与数据层之间。用户离线编辑文档草稿时的完整链路:状态管理器接到更新动作,不直接碰存储,而是调用数据层的写入方法;该方法开启本地数据库事务写入实体,同时把本次变更追加到变更日志并标记为待同步;随后派发实体写入事件,内核更新状态树,界面显示"正在同步"。网络恢复后,Worker 触发同步事件,协调器扫描变更日志中待同步的记录,构造请求发给后端;成功则更新状态并派发已同步事件;若返回版本冲突,则拉取最新版本做三方合并生成补丁再提交。整个过程对内核透明——它只关心事件,不关心实现。

这种设计本质上是事件溯源风格的前端数据流:每个用户操作都转化为一条不可变的事件记录。需要审计用户行为,回放事件流即可;需要时间旅行调试,重放特定时间点前的所有事件即可。架构的深度正在于此种设计赋予的可观测性与可追溯性。

三、能力契约:三层防御

PWA 的"渐进式"要能在老旧设备、企业防火墙、无 HTTPS 环境中优雅降级,靠的不是条件编译,而是一套三层的能力契约体系。

第一层,运行时能力探测。拒绝粗粒度的存在性判断,深入接口内部:探测导航预加载是否真正可用(注册后启用再看结果),探测后台同步的精确支持程度。每个探测项返回异步结果,输入到一张能力图谱——一个有向无环图,节点是能力,边是依赖关系(周期同步依赖后台同步)。某节点不可用时,其所有下游节点自动置灰,系统据此动态裁剪功能模块。

**第二层,声明式能力声明。**在构建配置中声明所需能力契约:必需能力、可选能力、以及每项能力不可用时的回退方式。构建工具读取声明,自动注入对应垫片或裁剪未声明能力的代码块。这使"渐进式"从运行时策略升维为构建时契约。

第三层,语义化降级路径。最精妙之处在于:降级不是功能消失,而是语义平移。角标接口不可用时,不简单隐藏角标,而是把设置角标的调用转换为在页面标题里插入数字、或用样式生成视觉角标;周期同步不可用时,退化为监听页面可见性变化——从后台切回前台立即触发一次刷新。"有新消息"这个语义从未丢失,只是载体从系统角标变为页面徽章再变为标题数字,用户心智模型保持一致。

层级 手段 解决什么
运行时探测 能力图谱加动态裁剪 真实环境的能力差异
声明式声明 构建时注入与裁剪 渐进式的工程化落地
语义化降级 语义平移而非功能删减 用户心智一致性

⚠️ 常见坑:让 Worker 兜售所有逻辑。一个常见反模式是在同步事件里拿到数据后发消息给主线程、再由主线程调接口提交——多余跳转且可能阻塞主线程。更优做法是让 Worker 直接承担网络请求与错误处理,只把最终结果(成功、失败、冲突)传回。

💡 关键直觉:三层的本质是责任边界的契约——外壳承诺速度,内核承诺正确,数据承诺一致。往后发展的方向也很清晰:健康探针持续监控首屏耗时与关键资源成功率,异常时自动回滚到上一稳定版缓存——架构从"防御"走向"自愈"。

五、演进路线:从单体到三层的迁移策略

存量项目很少能推倒重来,三层的落地需要一条渐进迁移路线。第一步,切出外壳。把应用首屏必需的骨架资源从构建产物里单独拎出来——页面结构、关键样式、最小脚本——为它们建立独立的缓存版本。这一步不动业务逻辑,只改资源组织,风险最低。验证标准是断网刷新时能看到完整框架(哪怕内容区是空态)。第二步,收拢数据访问。把散落在各组件里的本地存储调用,统一收口到数据层的实体接口后面。这一步的副产品是巨大的:接口收敛后,加变更日志、加同步协调器都只改一处。第三步,装上调度器。在内核层引入能力探测,把原先散落各处的"是否支持某接口"判断统一进能力图谱。第四步,才做离线写入与后台同步——此时三层契约已就位,最复杂的同步逻辑是在稳固地基上盖楼。

这条路线的每一步都独立可验证、可回滚,符合"渐进式"自己的哲学——用渐进式的方式落地渐进式架构,多少有点自洽的意味。迁移过程中最常见的诱惑是跳过第二步直接做第四步:离线写入直接写在组件里,短期跑得通,三个月后每个组件都拖着一小段同步逻辑,冲突处理五花八门,重构成本指数上升。

要点速记

  • 要点一:三层架构是对渲染速度、网络容错、持久化三对矛盾的回应,不是审美偏好。
  • 要点二:外壳的硬指标是零网速下三百毫秒出完整框架。
  • 要点三:数据层用变更日志驱动同步,天然支持审计与时间旅行调试。
  • 要点四:双轨响应在"可用"与"新鲜"之间动态平衡。
  • 要点五:能力契约三层——探测、声明、降级——让渐进式成为可编译的工程事实。
  • 要点六:降级要语义平移,不做功能切除。

蓝图有了,下一节讲施工:开发流程的三个环、缓存策略的代码权衡,以及从零构建的四阶实验。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U