手写 40 行响应式系统,读懂 Vue3 的 Proxy 和依赖收集
从 Vue2 的 defineProperty 说起:它监听不到数组下标和新增属性,才有了 $set 这个补丁。Vue3 换成 Proxy 后这些问题迎刃而解。这篇文章手写一个 40 行的迷你响应式系统,讲清楚 effect 注册副作用、track 在 get 时收集依赖、trigger 在 set 时派发更新,把 reactive / ref / effect 三者的关系一次说明白,附完整可运行代码。
这是明确标注的版式示例,用来验证长文、目录、图片、代码和表格。原项目文章仍属于示例内容,下面的代码也只用于说明排版。
从一个小问题开始
当一个界面跟随数据变化时,读者最容易理解的是数据发生了什么、界面为什么需要重新呈现。把复杂的术语先放在一边,我们可以从一段普通的对象和一条清楚的输出开始。
文章页应当让这些关系保持清楚:主标题给出主题,摘要说明阅读范围,正文逐步展开细节。目录不是另外一套内容,而是同一篇文章的路标。需要回头查找某个步骤时,读者能够直接到达对应位置。
先看数据,再看结果
下面是一个简单的展示片段。它没有网络请求,也不包含个人信息;你可以用复制按钮取走原始代码。
const article = {
title: '一篇示例文章',
published: true,
}
function describe(item) {
return item.published
? item.title + ':可阅读'
: item.title + ':草稿'
}
console.log(describe(article))
把问题拆成可以观察的步骤
- 找到需要改变的数据。
- 确定界面依赖哪些字段。
- 把更新放到明确的位置。
- 验证空值、错误和重复操作。
每个步骤都应该能解释自己的作用。遇到问题时,记录输入、观察结果,再缩小需要检查的范围。这比把所有细节混在一个巨大的步骤里更容易复核。
理解依赖关系
界面里的信息并不总是独立的。标题、分类、发布日期来自文章数据;目录来自正文的标题;阅读时间来自实际正文长度。一个字段改变时,只有真正依赖它的部分需要随之改变。
这也是组件拆分的理由:拆分不是让文件数量增加,而是让状态和责任容易看见。读取文章的逻辑负责区分加载、不存在和错误;正文渲染负责排版与安全;互动区域负责请求和反馈。
- 输入清楚:知道数据来自哪里。
- 状态明确:加载、成功、为空和失败有不同呈现。
- 反馈克制:让操作结果可见,但不打断阅读。
更新之前保留原状态
一些操作可以先给出即时反馈。例如点赞按钮会立即改变状态,随后等待服务端确认。如果请求失败,则恢复原状态并提供重试。真实数量始终以服务端回应为准。
这个页面在尚未连接互动服务时,不会假装读者已经点赞,也不会放上看似真实的评论。读者仍然可以复制链接、打开系统分享和放大图片,已经可用与还需要配置的功能应该容易区分。
用表格整理状态
表格适合对照几个重复的字段。手机屏幕不必把所有列挤成难以阅读的窄条;表格可以在自己的区域内左右滚动。
| 状态 | 页面反馈 | 是否保留输入 | 后续动作 |
|---|---|---|---|
| 正在读取文章 | 显示加载提示 | 不适用 | 等待返回 |
| 文章不存在 | 显示未找到文章 | 不适用 | 返回文章列表 |
| 点赞请求失败 | 恢复之前的状态 | 不适用 | 可以重试 |
| 评论提交失败 | 显示错误原因 | 保留昵称与正文 | 修改或重试 |
| 评论等待审核 | 明确标记待审核 | 服务端已收到 | 等待管理员处理 |
图片和说明放在一起
图片说明帮助读者知道自己在看什么。点击下面的项目现有素材可以查看放大效果;它只是一张版式测试图片,与文章主题不构成事实关联。

文字与图片之间留出自然间距,避免视觉上互相挤压。没有封面的文章不需要一个空的大框;正文里的图片加载失败时,也应该有明确的说明,而不是长久停留在空白处。
阅读时的几个小细节
阅读体验经常由很小的细节决定。标题与正文之间的距离、中文标点的节奏、代码的行距、链接的下划线,以及按钮能否用键盘找到,都会影响一篇长文是否让人愿意读完。
目录会提示当前章节,但不会强制跟随读者滚动。阅读进度与预计阅读时间放在右侧;在手机上,目录可以收起,互动区域也不会固定在输入框上方,因此不会遮住系统键盘。
链接与引用
外部资料使用正常的链接。比如 Markdown 的语法说明 可以作为扩展阅读。外部链接会标注浏览器打开方式,页面不统计无法核实的转发数量。
好的界面让内容、状态和操作各自清楚。读者无需猜测一个按钮有没有成功,也无需在错误之后重新输入整段文字。
结束之前
这篇示例把标题、引用、列表、表格、图片和代码放在了同一条阅读路径里。真实文章接入后,可以沿用这些排版规则而不必重新制作页面。
如果你想再看其他内容,可以通过顶部入口返回文章列表。收藏当前链接也能直接回到这篇文章;正式部署时需要配置站点域名与服务器的独立地址规则。