Vue3源码阅读(六)reactive

reactive

  • 实现源码在packsages/reactivity/reactive.ts

  • 图

  • 目录结构

── src
    ├── baseHandlers.ts // 基本类型的处理器
    ├── collectionHandlers.ts  // Set Map WeakSet WeckMap的处理器
    ├── computed.ts // 计算属性,同Vue2
    ├── effect.ts // reactive 核心,处理依赖收集,依赖更新
    ├── index.ts
    ├── operations.ts // 定义依赖收集,依赖更新的类型
    ├── reactive.ts // reactive 入口,内部主要以Proxy实现
    └── ref.ts // reactive 的变种方法,Proxy处理不了值类型的响应,Ref来处理
  • reactive
    • reactive是Vue3中响应数据核心
    • reactive 中的实现是由 proxy 加 effect 组合
    // reactive.ts
    export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
    export function reactive(target: object) {
    // 如果目标对象是一个只读的响应数据,则直接返回目标对象
    // if trying to observe a readonly proxy, return the readonly version.
        if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
            return target
        }
        // 否则调用createReactiveObject创建 observe
        return createReactiveObject(
            target,
            false,
            mutableHandlers,
            mutableCollectionHandlers,
            reactiveMap
        )
    }
    // createReactiveObject 
    function createReactiveObject(
        target: Target, // 目标对象
        isReadonly: boolean, // 是否只读
        baseHandlers: ProxyHandler<any>, // 基本类型的 handlers
        collectionHandlers: ProxyHandler<any>, // 主要对(set、map、weakSet、weakMap)的handlers
        proxyMap: WeakMap<Target, any> // 
    ) {
    // 如果不是对象,在开发模式抛出警告,生产环境直接返回目标对象
    if (!isObject(target)) {
        if (__DEV__) {
        console.warn(`value cannot be made reactive: ${String(target)}`)
        }
        return target
    }
    // target is already a Proxy, return it.
    // exception: calling readonly() on a reactive object
    // 如果目标对象已经是个proxy直接返回
    if (
        target[ReactiveFlags.RAW] &&
        !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
    ) {
        return target
    }
    // target already has corresponding Proxy
    // 如果目标对象已有代理返回代理对象
    const existingProxy = proxyMap.get(target)
    if (existingProxy) {
        return existingProxy
    }
    // only a whitelist of value types can be observed.
    // 如果目标对象是个不能被观察的对象直接返回
    const targetType = getTargetType(target)
    if (targetType === TargetType.INVALID) {
        return target
    }
    // 通过new Proxy创建 observe
    // 如果是普通的对象 Object 或 Array,处理器对象就使用 baseHandlers,如果是 Set, Map, WeakMap, WeakSet 中的一个,就使用 collectionHandlers。
    const proxy = new Proxy(
        target,
        targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
    )
    // 将原始对象和代理对象放入proxyMap
    proxyMap.set(target, proxy)
    return proxy
    }

    //获取观察种类  
   function getTargetType(value: Target) {
    // 被打上__v_skip标记或者改对象不可扩展(不可添加新的属性)返回0
    return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
        ? TargetType.INVALID
        : targetTypeMap(toRawType(value))
    }
    export const enum ReactiveFlags {
        SKIP = '__v_skip',
        IS_REACTIVE = '__v_isReactive',
        IS_READONLY = '__v_isReadonly',
        RAW = '__v_raw'
    }
    const enum TargetType {
        INVALID = 0,
        COMMON = 1,
        COLLECTION = 2
    } 
    // 根据类型判断是否可观察,可以被观察的:Object,Array,Map,Set,WeakMap,WeakSet
    function targetTypeMap(rawType: string) {
    switch (rawType) {
        case 'Object':
        case 'Array':
        return TargetType.COMMON
        case 'Map':
        case 'Set':
        case 'WeakMap':
        case 'WeakSet':
        return TargetType.COLLECTION
        default:
        return TargetType.INVALID
    }
    }
  • reactive 是做为整个响应式的入口,负责处理目标对象是否可观察以及是否已被观察的逻辑,最后使用 Proxy 进行目标对象的代理
  • 注意:
    • export const reactiveMap = new WeakMap<Target, any>(),reactiveMap使用的是 WeakMap,WeakMap 的键值必须是对象,而且 WeakMap 的键值是不可枚举的,是弱引用。方便垃圾回收。
    • 弱引用:当它的键所指对象没有被其他地方引用时,就会被垃圾回收了
      • 对应的是垃圾回收机制中的标记清除法
    • 强引用:当一个对象被创建时,计数为1,每有一个变量引用该对象,计数都会加1,只有当计数为0时,对象才会被垃圾回收。所以一旦造成循环引用等问题,就会造成内存泄漏。
      • 对应的是垃圾回收机制中的引用计数法

reactive.spec 单元测试

  • 单纯用来熟悉下Object变化的情况,熟悉可跳过
    test('Object', () => {
        const original = { foo: 1 }
        const observed = reactive(original)
        expect(observed).not.toBe(original)
        // isReactive检查一个对象是否是由 reactive创建的响应式代理。
        expect(isReactive(observed)).toBe(true)
        expect(isReactive(original)).toBe(false)
        // get
        expect(observed.foo).toBe(1)
        // has
        expect('foo' in observed).toBe(true)
        // ownKeys
        expect(Object.keys(observed)).toEqual(['foo'])
    })
    test('nested reactives', () => {
        const original = {
        nested: {
            foo: 1
        },
        array: [{ bar: 2 }]
        }
        const observed = reactive(original)
        // 嵌套的属性也可以响应
        expect(isReactive(observed.nested)).toBe(true)
        expect(isReactive(observed.array)).toBe(true)
        expect(isReactive(observed.array[0])).toBe(true)
    })
    test('observed value should proxy mutations to original (Object)', () => {
        //代理对象属性变化会同步至原始对象 
        const original: any = { foo: 1 }
        const observed = reactive(original)
        // set
        observed.bar = 1
        expect(observed.bar).toBe(1)
        expect(original.bar).toBe(1)
        // delete
        delete observed.foo
        expect('foo' in observed).toBe(false)
        expect('foo' in original).toBe(false)
    })
    test('setting a property with an unobserved value should wrap with reactive', () => {
        // 给observed设置一个未被观察的值依然是响应式的(修复vue2.x中的问题)
        const observed = reactive<{ foo?: object }>({})
        const raw = {}
        observed.foo = raw
        expect(observed.foo).not.toBe(raw)
        expect(isReactive(observed.foo)).toBe(true)
    })
    test('observing already observed value should return same Proxy', () => {
        // 观察一个已经被observed的observe应该直接返回该observe
        const original = { foo: 1 }
        const observed = reactive(original)
        const observed2 = reactive(observed)
        expect(observed2).toBe(observed)
    })
   test('observing the same value multiple times should return same Proxy', () => {
        // 观察已被observed的对象直接返回代理对象
        const original = { foo: 1 }
        const observed = reactive(original)
        const observed2 = reactive(original)
        expect(observed2).toBe(observed)
    })
    test('should not pollute original object with Proxies', () => {
        // 修改代理对象的值不会污染原始对象
        const original: any = { foo: 1 }
        const original2 = { bar: 2 }
        const observed = reactive(original)
        const observed2 = reactive(original2)
        observed.bar = observed2
        expect(observed.bar).toBe(observed2)
        expect(original.bar).toBe(original2)
    })
    test('unwrap', () => {
        const original = { foo: 1 }
        const observed = reactive(original)
        // toRaw返回被观察对象的原始对象
        expect(toRaw(observed)).toBe(original)
        expect(toRaw(original)).toBe(original)
    })
    test('should not unwrap Ref<T>', () => {
        // 返回值为Ref
        const observedNumberRef = reactive(ref(1))
        const observedObjectRef = reactive(ref({ foo: 1 }))
        expect(isRef(observedNumberRef)).toBe(true)
        expect(isRef(observedObjectRef)).toBe(true)
    })
    // 测试各类型能否被观察
    test('non-observable values', () => {
        const assertValue = (value: any) => {
            reactive(value)
            expect(
            `value cannot be made reactive: ${String(value)}`
            ).toHaveBeenWarnedLast()
        }
        // number
        assertValue(1)
        // string
        assertValue('foo')
        // boolean
        assertValue(false)
        // null
        assertValue(null)
        // undefined
        assertValue(undefined)
        // symbol
        const s = Symbol()
        assertValue(s)
        // built-ins should work and return same value
        const p = Promise.resolve()
        expect(reactive(p)).toBe(p)
        const r = new RegExp('')
        expect(reactive(r)).toBe(r)
        const d = new Date()
        expect(reactive(d)).toBe(d)
    })
    test('markRaw', () => {
        // markRaw 可以给将要被观察的数据打上标记,标记原始数据不可被观察
        // 标记不可被观察的数据不可观察
        const obj = reactive({
            foo: { a: 1 },
            bar: markRaw({ b: 2 })
        })
        expect(isReactive(obj.foo)).toBe(true)
        expect(isReactive(obj.bar)).toBe(false)
    })
    //被freeze冻结的对象不可观察 
    test('should not observe frozen objects', () => {
        const obj = reactive({
            foo: Object.freeze({ a: 1 })
        })
        expect(isReactive(obj.foo)).toBe(false)
    })
    // shallowReactive:只为某个对象的私有(第一层)属性创建浅层的响应式代理,不会对"属性的属性"做深层次、递归地响应式代理
    test('should keep reactive properties reactive', () => {
        // 属性的属性不可观察
        const props: any = shallowReactive({ n: reactive({ foo: 1 }) })
        props.n = reactive({ foo: 2 })
        expect(isReactive(props.n)).toBe(true)
    })
    test('should keep reactive properties reactive', () => {
        // shallowReactive后的proxy的属性若再次被reactive则可被观察
        const props: any = shallowReactive({ n: reactive({ foo: 1 }) })
        props.n = reactive({ foo: 2 })
        expect(isReactive(props.n)).toBe(true)
    })
    test('should not observe when iterating', () => {
        // 通过...迭代出不可观察
        const shallowSet = shallowReactive(new Set())
        const a = {}
        shallowSet.add(a)
        const spreadA = [...shallowSet][0]
        expect(isReactive(spreadA)).toBe(false)
    })
    test('should not get reactive entry', () => {
        // get到的属性不可观察
        const shallowMap = shallowReactive(new Map())
        const a = {}
        const key = 'a'
        shallowMap.set(key, a)
        expect(isReactive(shallowMap.get(key))).toBe(false)
    })
    test('should not get reactive on foreach', () => {
        // 循环出的每一项不可观察
        const shallowSet = shallowReactive(new Set())
        const a = {}
        shallowSet.add(a)
        shallowSet.forEach(x => expect(isReactive(x)).toBe(false))
    })
// shallowRef可以强制触发更新
test('shallowRef force trigger', () => {
  const sref = shallowRef({ a: 1 })
  let dummy
  effect(() => {
    dummy = sref.value.a
  })
  expect(dummy).toBe(1)
  sref.value.a = 2
  expect(dummy).toBe(1) // should not trigger yet
  // force trigger
  triggerRef(sref)
  expect(dummy).toBe(2)
})
//isRef判断是否为ref 
test('isRef', () => {
  expect(isRef(ref(1))).toBe(true)
  expect(isRef(computed(() => 1))).toBe(true)
  expect(isRef(0)).toBe(false)
  expect(isRef(1)).toBe(false)
  expect(isRef({ value: 0 })).toBe(false)
})
// customRef支持自定义 ref, 自由控制 track, trigger 时间
test('customRef', () => {
  let value = 1
  let _trigger: () => void
  const custom = customRef((track, trigger) => ({
    get() {
      track()
      return value
    },
    set(newValue: number) {
      value = newValue
      _trigger = trigger
    }
  }))
  expect(isRef(custom)).toBe(true)
  let dummy
  effect(() => {
    dummy = custom.value
  })
  expect(dummy).toBe(1)
  custom.value = 2
  // should not trigger yet
  expect(dummy).toBe(1)
  _trigger!()
  expect(dummy).toBe(2)
})
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值