教育类 App 与电商、社交类产品有一个显著差异:用户使用场景往往发生在网络不稳定的环境里。学生在通勤路上看录播课、在教室里做练习、在宿舍熄灯后复习错题,这些时刻网络质量都不理想。如果 App 把每次交互都绑定在请求-响应模型上,体验会非常糟糕。这篇文章讨论我在教育 App 开发中采用的离线优先架构,以及它带来的实际收益。

为什么教育场景天然适合离线优先
先看几类典型数据:
- 课程内容:视频、音频、讲义、练习题,变更频率低,适合预下载。
- 学习进度:做题记录、观看进度、收藏错题,产生频繁但允许延迟上报。
- 用户配置:主题、提醒时间、学习计划,几乎只在本地读写。
这三类数据的共同点是:读多写少,且写操作可以合并。这意味着本地数据库完全可以作为主数据源,服务端只承担同步与备份职责。把「先请求再渲染」改成「先读本地再后台同步」,首屏时间可以从秒级降到毫秒级。
本地数据层的选型
移动端我用 SQLite 做结构化存储,配合一层轻量 ORM。对于题目、知识点这类带关联查询的数据,SQLite 的索引能力比键值存储更合适。下面是题目表的核心结构:
CREATE TABLE question (
id TEXT PRIMARY KEY,
course_id TEXT NOT NULL,
stem TEXT NOT NULL,
options TEXT NOT NULL,
answer TEXT NOT NULL,
updated_at INTEGER NOT NULL,
sync_state INTEGER DEFAULT 0
);
CREATE INDEX idx_question_course ON question(course_id);
CREATE INDEX idx_question_sync ON question(sync_state);
sync_state 是关键字段:0 表示已同步,1 表示本地新增待上传,2 表示本地修改待上传,3 表示本地删除待同步。所有写操作先落本地,再由同步器批量处理,UI 永远不等待网络。
同步策略:时间戳加操作日志
只靠 updated_at 做增量拉取会遇到一个问题:多设备同时修改同一条记录时,后写入的会覆盖先写入的。教育 App 里最典型的是错题本——手机和平板各删了一道题,简单覆盖会导致数据不一致。
我的做法是服务端维护一个递增的版本号,客户端拉取时带上 since_version,服务端返回该版本之后的所有变更。上传时按操作类型区分:
- 新增:以客户端生成的 UUID 为主键,服务端幂等插入。
- 修改:携带字段级 diff,服务端按字段合并,冲突时以学习进度更靠后的为准。
- 删除:软删除,保留墓碑记录,避免同步过程中被旧数据复活。
async function flushPending() {
const rows = await db.query(
'SELECT * FROM question WHERE sync_state IN (1,2,3) LIMIT 100'
);
if (!rows.length) return;
const res = await api.pushChanges(rows.map(toPayload));
await db.transaction(async (tx) => {
for (const item of res.accepted) {
await tx.run('UPDATE question SET sync_state = 0 WHERE id = ?', [item.id]);
}
});
}
批量大小控制在 100 条以内,失败时保留 sync_state,下次触发再重试,不会丢数据。
媒体资源的预下载与淘汰
视频和音频不适合塞进 SQLite,我用文件系统加一张资源索引表管理。下载队列按「最近学习课程优先、未完成课程次之」排序,并限制并发数为 2,避免占满带宽影响同步请求。
淘汰策略按 LRU 加课程状态:已完成的课程资源在磁盘占用超过阈值时优先清理,正在学习的课程保留。索引表里记录 last_access 和 file_size,清理时按访问时间升序删除,直到释放出足够空间。
建议在设置页给用户一个「仅 Wi-Fi 下载」开关,并展示当前缓存占用。教育类用户对流量敏感,透明化能明显降低卸载率。
冲突与一致性要提前想清楚
离线优先不是把同步逻辑藏起来,而是把冲突处理规则显式定义出来。我的原则是:学习进度类数据以「最大值」合并,收藏与错题以「并集」合并,笔记类内容冲突时保留两份并提示用户选择。规则写进文档,前后端按同一套规则实现,联调成本会低很多。
另外,首次安装后的初始化同步要做成分页拉取,每页 200 条,边拉边渲染,避免用户对着白屏等待。进度条按已拉取条数估算,比转圈动画更能安抚等待情绪。
收益与代价
这套架构上线后,课程列表页的冷启动从平均 1.8 秒降到 300 毫秒以内,弱网环境下的练习提交成功率接近 100%。代价是客户端代码量增加约三成,同步模块需要单独测试,包括断网、重复提交、服务端回滚等场景。
如果团队规模小、课程内容以直播为主,可以先只做「进度与错题离线」,把视频预下载放到二期。离线优先不是全有或全无的选择,按数据特性分层落地,投入产出比更可控。

评论 (0)
还没有评论,快来抢沙发吧~