脚本宝典收集整理的这篇文章主要介绍了2.实现reactive和effect,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
reactive在vue中对象经过包裹后返回一个新的响应对象,主要原理是在get时进行依赖收集,在set是进行触发依赖,在2使用Object.definePRoPErty进行实现,在3使用Proxy对象进行改写实现方法,接下来我们使用proxy来实现reactive。
effect在vue中是一个响应函数,当函数内部的响应式对象发生改变时,effect会重新触发,主要实现原理是在effect第一次触发的时候,函数的响应式对象会触发自身的get方法,从而把effect收集起来,当响应式对象改变时,会把自身收集的effect函数重新触发。
首先在tsconfig.json中添加lib字段来支持DOM和es6
"lib": ["DOM","ES6"]
创建以下文件
src
└── reactivITy
├── tests
│ └── effect.spec.ts
│ └── reactive.spec.ts
└── effect.ts
└── reactive.ts
在reactive下编写reactive方法并导出,方法的作用是:传进入一个对象,返回一个使用Proxy包裹的响应式对象。
后续在get的时候进行依赖收集,在set时候进行触发依赖。
// reactive.tsexport function reactive(raw) { return new Proxy(raw, { get(target, key) { const res = Reflect.get(target, key) // TODO 收集依赖 return res }, set(target, key, value) { const res = Reflect.set(target, key, value) // TODO 触发依赖 return res } }); }
编写测试代码进行测试,执行yarn jest命令
// reactive.spec.ts import { reactive } From '../reactive' describe('reactive', () => { it('happy path', () => { const user = { name: 'zs' } const obj = reactive(user) expect(obj.name).toBe('zs') // 响应式包裹的对象不等于原始对象 expect(obj).not.toBe(user) }) })
在effect文件下创建effect方法并导出,创建ReactiveEffect类将fn方法进行保存,在effect方法中初始化类并执行run方法。
// effect.ts class ReactiveEffect { private _fn: any constructor(fn) { // 保存fn函数 this._fn = fn } run() { this._fn() } } export function effect(fn) { const _effect = new ReactiveEffect(fn) // 第一次就执行 _effect.run() }
编写测试代码进行测试,执行yarn jest命令,得到成功结果
// effect.spec.ts import { effect } from '../effect' import { reactive } from '../reactive' describe('effect', () => { it('happy path', () => { const user = reactive({ age: 10 }) let nextAge; effect(() => { // 第一次就执行该函数 nextAge = user.age + 1 }) expect(nextAge).toBe(11) }) })
接下来完善代码,实现依赖收集和触发
创建track方法收集依赖,使用activeEffect全局变量存储当前的effect函数
// effect.ts // 修改ReactiveEffect class ReactiveEffect { private _fn: any constructor(fn) { this._fn = fn // TODO 新加的一行代码,用于存储当前执行的effect函数 activeEffect = this } run() { this._fn() } } const effectMap = new Map() // 全局变量,用于获得当前的fn函数 let activeEffect export function track(target, key) { // 在effectMap中查找 let depsMap = effectMap.get(target) // effectMap中不存在,创建一个Map并把target当key,Map当value if (!depsMap) { depsMap = new Map() effectMap.set(target, effect) } // 在depsMap中查找key let deps = depsMap.get(key) if (!deps) { deps = new Set() depsMap.set(key, deps) } // 将正在执行的effect函数添加到deps中 deps.add(activeEffect) }
创建trigger方法触发所有存储的依赖
// effect.ts export function trigger(target, key) { // 从map中查找,取出所有依赖并执行 const depsMap = effectMap.get(target) const deps = depsMap.get(key) for (let dep of deps) { dep.run() } }
最后在reactive中添加方法
import { track, trigger } from "./effect" export function reactive(raw) { return new Proxy(raw, { get(target, key) { ... track(target, key) ... }, set(target, key, value) { ... trigger(target,key) ... } }); }
编写测试代码进行测试,执行yarn jest命令,得到成功结果
// effect.spec.ts import { effect } from '../effect' import { reactive } from '../reactive' describe('effect', () => { it('happy path', () => { const user = reactive({ age: 10 }) let nextAge; effect(() => { nextAge = user.age + 1 }) expect(nextAge).toBe(11) user.age++ expect(nextAge).toBe(12) }) })
以上是脚本宝典为你收集整理的2.实现reactive和effect全部内容,希望文章能够帮你解决2.实现reactive和effect所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。