Vue3.0 响应式系统(三) reactive readonly

从这篇文章起,我们看看Vue3.0的响应式部分源码。这部分reactive方法是核心,reactive方法如下:

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
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers
  )
}

首先判断传给reactive方法的参数是否已经被readonly方法处理过,如果已经被处理过,则直接返回。我们先看看被readonly处理过的例子:

​
<html>
  <head>
  </head>
  <body>
    <div id="app">
      <div>hello {{state.student.name}}</div>
    </div>
    <script src="../dist/vue.global.js"></script>
    <script>
      const {createApp, reactive, toRef, toRefs, ref, readonly} = Vue
      const app = createApp({
        setup(){       
          let obj = {
            msg: 'hello',
            student: {
              name: 'liubbc',
              age: 20
            }
          }
         
          let objReadonly = readonly(obj)
          //传给reactive方法的参数已被readonly处理过
          let state = reactive(objReadonly)     
          return {state}
        }
      }).mount('#app')      
      setTimeout(() => {
        console.log(`name is: ${app.state.student.name}`)
        app.state.student.name = 'world'        
      }, 2000)
    </script>
  </body>
</html>

继续往下走,看createReactiveObject方法:

function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<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
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  const proxyMap = isReadonly ? readonlyMap : reactiveMap
  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
  }
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)
  return proxy
}

1. 首先判断要进入reactive的对象是否为object,如果不是object则直接返回,在开发环境下给个警告。通过例子看看哪些类型会直接返回:

          let num = 10 
          console.log(`num will be directly return: ${reactive(num)}`) 
          let str = 'abc' 
          console.log(`str will be directly return: ${reactive(str)}`) 
          let bool = true 
          console.log(`bool will be directly return: ${reactive(bool)}`)
          let undef = undefined 
          console.log(`undef will be directly return: ${reactive(undef)}`)
          let symbol = Symbol()
          reactive(symbol)

看下打印:

这几种基本类型传给reactive 都没有作用,直接返回了。

2. 如果传个reactive的是个Proxy对象,那么也会直接返回:

  // target is already a Proxy, return it.
  // exception: calling readonly() on a reactive object
  // 对已经进行reactive的对象,取ReactiveFlags.RAW 属性会返回true,因为进行reactive的过程
  // 中会用weakMap进行保存,通过target能判断出是否有ReactiveFlags.RAW属性
  // 这里有个例外就是,对reactive对象进行readonly是合法的。

  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }

我们也通过例子看下这种情况:

        setup(){       
          let obj = {
            msg: 'hello',
            student: {
              name: 'liubbc',
              age: 20
            }
          }
          let objReactive = reactive(obj)
          let state = reactive(objReactive)    
          return {state}
        }

        setup(){       
          let obj = {
            msg: 'hello',
            student: {
              name: 'liubbc',
              age: 20
            }
          }
          let objReadonly = readonly(obj)
          let state = readonly(objReadonly)    
          return {state}
        }

也就是重复进行reactive 进行 reactive是没有意义的,直接返回。但有一种例外就是:

       setup(){       
          let obj = {
            msg: 'hello',
            student: {
              name: 'liubbc',
              age: 20
            }
          }
          let objReactive = reactive(obj)
          let state = readonly(objReactive)    
          return {state}
        }

先进行reactive,再对reactive对象进行readonly 是可以的。

3. 接着往下看,

  
  export const reactiveMap = new WeakMap<Target, any>()
  export const readonlyMap = new WeakMap<Target, any>()


  // target already has corresponding Proxy
  const proxyMap = isReadonly ? readonlyMap : reactiveMap
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }

这个地方挺关键的,对已经Proxy的,则直接从WeakMap数据结构中取出这个Proxy对象,这是提升性能的一种方法。从代码中可以看出后面会对Proxy对象会进行proxyMap.set(target, proxy) 操作。

4. 接着往下走,后面还对可以reactive的对象加了个白名单,只有Object,Array,Map,Set,WeakMap,WeakSet对象可以进行reactive操作

  
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
  }
}

function getTargetType(value: Target) {
  return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
    ? TargetType.INVALID
    : targetTypeMap(toRawType(value))
}



  // only a whitelist of value types can be observed.
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }


5. 看看最后一段代码:

  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)

最后进行了 Proxy,注意不管target有多深,在初次进行reactive的时候只对第一层进行Proxy。我们看下reactive后的Proxy对象:

对比vue2.0,可以看出Proxy只对第一层进行reactive操作,也就是进行了延迟reactive操作,而不是无脑进行全部reactive操作,提升了性能。

 

我们看看readonly部分,

export function readonly<T extends object>(
  target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
  return createReactiveObject(
    target,
    true,
    readonlyHandlers,
    readonlyCollectionHandlers
  )
}

readonly直接调用createReactiveObject,只是传的参数不一样。经过readonly处理过的对象,对象的所有属性都是不可更改的。看个简单例子:

​
<html>
  <head>
  </head>
  <body>
    <div id="app">
      <div>hello {{state.student.name}}</div>
    </div>
    <script src="../dist/vue.global.js"></script>
    <script>
      const {createApp, reactive, toRef, toRefs, ref, readonly} = Vue
      const app = createApp({
        setup(){       
          let obj = {
            msg: 'hello',
            student: {
              name: 'liubbc',
              age: 20
            }
          }
          
          let state = readonly(obj)    
          return {state}
        }
      }).mount('#app')      
      setTimeout(() => {
        console.log(`name is: ${app.state.student.name}`)
        app.state.student.name = 'world'        
      }, 2000)
    </script>
  </body>
</html>

修改app.state.student.name = 'world'  不起作用,在开发环境下给了个警告:

 

感觉在实际开发中readonly应该不常用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值