likes
comments
collection
share

一起学Vue3源码,实现最简Vue3【02】 - 实现 effect & reactive & 依赖收集 & 触发依赖

作者站长头像
站长
· 阅读数 12

实现 effect & reactive & 依赖收集 & 触发依赖

前言

上一章大致讲述了项目初始化,本章主要内容:实现 effect & reactive & 依赖收集 & 触发依赖,让我们一起来看看吧。


一、TDD是什么?

TDD现在是社区很火的一种开发方式。测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。

二、开始

1.搭建项目架构

1、先在src下创建 reactivity 文件夹用来实现 reactivity 响应式 相关逻辑 具体代码结构如下:

.
├── README.md
├── package.json
├── babel.config.js
├── src                  源码
│   └── reactivity
│       ├── src          reactivity 实现文件
│       └── tests        reactivity 测试文件
└── tsconfig.json        ts配置文件

2.测试文件

1、首先,先创建一个effect.spec.ts 文件用来写effect测试

import { effect } from "../src/effect"
import { reactive } from "../src/reactive"

describe('effect', () => {
    it('happy path', () => {
        // 创建一个reactive响应式数据
        const reactiveUser = reactive({
            age: 10
        })

        let newAge
        // 通过effect 去实现依赖的收集和触发
        effect(() => {
            newAge = reactiveUser.age + 1
        })

        expect(newAge).toBe(11) // expect 断言newAge变量 等于11

        // update age
        reactiveUser.age ++  // 更新响应式数据属性
        expect(newAge).toBe(12) // 断言newAge变量 等于12
    })
})

因为effect 整个测试流程很大,所以拆分出两块,分别是effect和reactive 测试,下面写一下reactive 测试:

import { reactive } from '../src/reactive';

describe('reactive', () => {
    it('happy path', () => {
        const original = { foo: 1 } // 声明初始变量original
        const observed = reactive(original) // 通过reactive使original变成响应式并赋值给observed
        expect(observed).not.toBe(original) // 因为vue3响应式是用Proxy实现的,所以断言observed 不等于 original
        expect(observed.foo).toBe(1) // 断言observed的foo 属性等于 1
    })
})

3.实现文件

import { track, trigger } from "./effect"

export function reactive(raw) {
    return new Proxy(raw, {
        get(target, key) {
            const res = Reflect.get(target, key)

            // 收集依赖
            track(target, key)
            return res
        },

        set(target, key, value) {
            const res = Reflect.set(target, key, value)

            // 触发依赖
            trigger(target, key)
            return res
        }
    })
}

effect 具体实现思路为下面的脑图

一起学Vue3源码,实现最简Vue3【02】 - 实现 effect & reactive & 依赖收集 & 触发依赖

// 全局的effect指针
let activeEffect

// effect响应式类
class ReactiveEffect {
    private _fn: any

    constructor(fn) {
        this._fn = fn
    }

    run() {
        activeEffect = this // 把全局effect赋值为当前effect实例
        this._fn() // 执行effect传过来的函数
    }
}

// 全局的target容器
let targetsMap = new Map()

// 收集依赖
function track(target, key) {
    // target => key => dep
    // target 目标对象
    // key 目标对象中的属性
    // dep 属性关联的函数(不可以重复,所以用Set存放)
    let depsMap = targetsMap.get(target)
    // 初始化时,depsMap不存在,要声明并且存放到对象Map中
    if (!depsMap) {
        depsMap = new Map()
        targetsMap.set(target, depsMap)
    }

    let dep = depsMap.get(key)
    // 初始化时,dep不存在,要声明并且存放到属性依赖Set中
    if (!dep) {
        dep = new Set()
        depsMap.set(key, dep)
    }
    dep.add(activeEffect) // 把当前effect实例收集起来
}

// 触发依赖
function trigger(target, key) {
    let depsMap = targetsMap.get(target) // 获取到相应的对象Map
    let dep = depsMap.get(key) // 获取到相应的属性依赖Set
    for (const effect of dep) {
        effect.run() // 去触发依赖Set中每个依赖
    }
}

// 执行函数
function effect(fn) {
    const _effect = new ReactiveEffect(fn) // 面向对象思想

    _effect.run()
}

export {
    track,
    trigger,
    effect
}

以上,执行jest,就通过了,如下图 一起学Vue3源码,实现最简Vue3【02】 - 实现 effect & reactive & 依赖收集 & 触发依赖


总结

本章讲述effect & reactive & 依赖收集 & 触发依赖的实现,一起学习Vue3。 坚持就是胜利,各位观众老爷的三连也是up的坚持动力,共勉! 最后,附上git地址:mini-vue

转载自:https://juejin.cn/post/7206950454098133048
评论
请登录