手写 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))

把问题拆成可以观察的步骤

  1. 找到需要改变的数据。
  2. 确定界面依赖哪些字段。
  3. 把更新放到明确的位置。
  4. 验证空值、错误和重复操作。

每个步骤都应该能解释自己的作用。遇到问题时,记录输入、观察结果,再缩小需要检查的范围。这比把所有细节混在一个巨大的步骤里更容易复核。

理解依赖关系

界面里的信息并不总是独立的。标题、分类、发布日期来自文章数据;目录来自正文的标题;阅读时间来自实际正文长度。一个字段改变时,只有真正依赖它的部分需要随之改变。

这也是组件拆分的理由:拆分不是让文件数量增加,而是让状态和责任容易看见。读取文章的逻辑负责区分加载、不存在和错误;正文渲染负责排版与安全;互动区域负责请求和反馈。

  • 输入清楚:知道数据来自哪里。
  • 状态明确:加载、成功、为空和失败有不同呈现。
  • 反馈克制:让操作结果可见,但不打断阅读。

更新之前保留原状态

一些操作可以先给出即时反馈。例如点赞按钮会立即改变状态,随后等待服务端确认。如果请求失败,则恢复原状态并提供重试。真实数量始终以服务端回应为准。

这个页面在尚未连接互动服务时,不会假装读者已经点赞,也不会放上看似真实的评论。读者仍然可以复制链接、打开系统分享和放大图片,已经可用与还需要配置的功能应该容易区分。

用表格整理状态

表格适合对照几个重复的字段。手机屏幕不必把所有列挤成难以阅读的窄条;表格可以在自己的区域内左右滚动。

状态 页面反馈 是否保留输入 后续动作
正在读取文章 显示加载提示 不适用 等待返回
文章不存在 显示未找到文章 不适用 返回文章列表
点赞请求失败 恢复之前的状态 不适用 可以重试
评论提交失败 显示错误原因 保留昵称与正文 修改或重试
评论等待审核 明确标记待审核 服务端已收到 等待管理员处理

图片和说明放在一起

图片说明帮助读者知道自己在看什么。点击下面的项目现有素材可以查看放大效果;它只是一张版式测试图片,与文章主题不构成事实关联。

版式测试图:来自项目已有首页图片素材
项目已有素材,仅用于验证图片说明与放大

文字与图片之间留出自然间距,避免视觉上互相挤压。没有封面的文章不需要一个空的大框;正文里的图片加载失败时,也应该有明确的说明,而不是长久停留在空白处。

阅读时的几个小细节

阅读体验经常由很小的细节决定。标题与正文之间的距离、中文标点的节奏、代码的行距、链接的下划线,以及按钮能否用键盘找到,都会影响一篇长文是否让人愿意读完。

目录会提示当前章节,但不会强制跟随读者滚动。阅读进度与预计阅读时间放在右侧;在手机上,目录可以收起,互动区域也不会固定在输入框上方,因此不会遮住系统键盘。

链接与引用

外部资料使用正常的链接。比如 Markdown 的语法说明 可以作为扩展阅读。外部链接会标注浏览器打开方式,页面不统计无法核实的转发数量。

好的界面让内容、状态和操作各自清楚。读者无需猜测一个按钮有没有成功,也无需在错误之后重新输入整段文字。

结束之前

这篇示例把标题、引用、列表、表格、图片和代码放在了同一条阅读路径里。真实文章接入后,可以沿用这些排版规则而不必重新制作页面。

如果你想再看其他内容,可以通过顶部入口返回文章列表。收藏当前链接也能直接回到这篇文章;正式部署时需要配置站点域名与服务器的独立地址规则。