实现最小版本的 ref 和 effect
2025-09-14 13:17:28
上一篇补充了链表的基础概念。
这一篇先实现一个最小闭环,主要是增加 vitest
官方行为
先用官方 Vue 看一下 ref 和 effect 的基础表现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reactivity Demo</title>
</head>
<body>
<script type="module">
import { effect, ref } from '../../../node_modules/vue/dist/vue.esm-browser.js'
const count = ref(0)
effect(() => {
console.log('count.value', count.value)
})
setTimeout(() => {
count.value++
}, 1000)
</script>
</body>
</html>
控制台会输出:
count.value 0
count.value 1
这里发生了两件事:
effect注册后会立即执行一次,所以先输出0count.value++修改了响应式数据,所以依赖它的effect会重新执行,再输出1
文件结构
在 packages/reactivity/src 下先创建三个文件:
packages/reactivity/src/
effect.ts
ref.ts
index.ts
它们的职责分别是:
effect.ts:管理副作用函数的注册、执行和依赖收集ref.ts:实现ref,在value的get和set中完成追踪与触发index.ts:统一导出对外 API
单元测试
先安装测试工具:
pnpm add vitest -D -w
项目后面会用到 @vue/shared,所以先在 vitest.config.ts 里把别名配置好:
import { resolve } from 'node:path'
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true
},
resolve: {
alias: {
'@vue/shared': resolve(import.meta.dirname, 'packages/shared/src')
}
}
})
然后新建测试文件:
packages/reactivity/__tests__/ref.spec.ts
测试先只覆盖两个能力:
ref可以创建带有value的响应式对象value变化后,读取过它的effect会重新执行
import { describe, expect, it } from 'vitest'
import { effect, ref } from '../src'
describe('ref', () => {
it('ref is defined', () => {
expect(ref).toBeDefined()
})
it('ref 是响应式的', () => {
const count = ref(0)
expect(count.value).toBe(0)
let dummy
effect(() => {
dummy = count.value
})
expect(dummy).toBe(0)
count.value++
expect(dummy).toBe(1)
})
})
此时执行测试会报错,因为 ref 和 effect 还没有实现。
视频
有小伙伴的自动录制,我截取了一下(但没声音)。但是最小版本我觉得问题不大