Skip to content
On this page

reactive解读

1:reactive方法是在packages\reactivity\src\reactive.ts 产生的 核心函数是createReactiveObject

js
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (isReadonly(target)) {
    return target
  }
  
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}

2: createReactiveObject接收五个参数,

js
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  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
  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 specific value types can be observed.
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  // debugger
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)
  return proxy
}

2-1:首先判断target是否是一个对象,如果不是,抛出警告,不执行往下流程,返回target内容

js
if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
}

2-2: 判断传入对象是属于什么类型,如果不是Object,Array,Map,Set,WeakMap,WeakSet 会中断下面流程,返回target

js
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}

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

2-3:接着就是执行new Proxy,定义响应式属性了

js
const proxy = new Proxy(
  target,
  targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
  • 判断target的类型是否Map,Set,WeakMap,WeakSet, 如果是 使用collectionHandlers,否则 使用baseHandlers

  • collectionHandlers 是对Map,Set,WeakMap,WeakSet类型进行处理 该函数定义了get, get size, has, add, set, delete, clear,forEach, keys, values, entrires, Symbol.iterator

    里面就会涉及到依赖的收集和依赖执行的函数,可以看到Map,Set,WeakMap,WeakSet,处理的东西比Object, Array复杂很多

js
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
  get: /*#__PURE__*/ createInstrumentationGetter(false, false)
}

function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
  const instrumentations = shallow
    ? isReadonly
      ? shallowReadonlyInstrumentations
      : shallowInstrumentations
    : isReadonly
    ? readonlyInstrumentations
    : mutableInstrumentations

  return (
    target: CollectionTypes,
    key: string | symbol,
    receiver: CollectionTypes
  ) => {
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    } else if (key === ReactiveFlags.RAW) {
      return target
    }

    return Reflect.get(
      hasOwn(instrumentations, key) && key in target
        ? instrumentations
        : target,
      key,
      receiver
    )
  }
}

  • baseHandles 定义了get, set,has,deleteProperty,ownKeyspackages\reactivity\src\baseHandlers.ts 这个就是就是响应式的代码,是的对象取值,设值,都会走 里面的方法
js
export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}

reactive和ref的关系

  • reactive和ref同时调用了reactive(value)函数,设置定义的属性

  • ref是一个构造函数,通过get value取值, set value设值, 在constructor内部实现的属性响应式是调用reactive方法

js
class RefImpl<T> {

  constructor(value: T, public readonly __v_isShallow: boolean) {
    this._rawValue = __v_isShallow ? value : toRaw(value)
    this._value = __v_isShallow ? value : toReactive(value)
  }

  get value() {
    trackRefValue(this)
    return this._value
  }

  set value(newVal) {
    // debugger
    const useDirectValue =
      this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
      // debugger
    newVal = useDirectValue ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = useDirectValue ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal)
    }
  }
}
  • ref的好处是可以设置基本类型number, string, boolean和对象 但是reactive只能设置对象作为一个响应式属性,如果是基本类型数据,不做处理

  • ref通过可以赋值替换(既:test.value = xxx),因为这个是触发了set value方法,可以触发依赖更新 但是对reactive的值赋值替换,这不会触发依赖的更新,应为引用已经改变了,又没有重新定义set, get操作符 来收集依赖的机制

js
<div>{{ test }}</div>
var { createApp, reactive  } = Vue;

var test = reactive({name: 1})

// 不会触发更新了
test = { name: 2 }

总结

reactive是Vue3的一个非常核心功能,里面就是为属性设置Proxy,为属性添加get,set操作符 在触发get函数的时候, 对依赖进行收集,在触发set函数的时候,执行所收集的依赖 从而达到数据更新,DOM视图也可以更新,既数据响应式

相关代码

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="../../dist/vue.global.js"></script>
</head>
<body>
  <div id="app">
    {{ test }}
  </div>
  <script>  
    var { createApp, reactive  } = Vue;

    var app = createApp({
        setup() {
            var test = reactive({name: 0});

            setInterval(() => {
              test.name++
            }, 3000)

            

            return {
                test,
            }
        }
    })
    app.mount('#app')

  </script>
  <script>
  </script>
</body>
</html>