阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 响应式代理的创建过程

响应式代理的创建过程

作者:陈川 阅读数:36425人阅读 分类: Vue.js

响应式代理的核心概念

Vue3的响应式系统基于Proxy实现,与Vue2的Object.defineProperty相比有显著改进。Proxy可以拦截对象的各种操作,包括属性访问、赋值、删除等,这使得Vue能够更精确地追踪依赖和触发更新。响应式代理的创建过程主要发生在reactive()函数调用时。

const obj = reactive({ count: 0 })

reactive()函数的执行流程

当调用reactive()时,Vue会执行以下步骤:

  1. 首先检查传入的值是否已经是响应式代理,如果是则直接返回
  2. 检查值类型,只有对象类型才能被代理
  3. 通过Proxy构造函数创建代理对象
  4. 设置代理对象的标识属性__v_isReactive
function reactive(target) {
  // 如果已经是响应式代理,直接返回
  if (target && target.__v_isReactive) {
    return target
  }
  
  // 只能代理对象
  if (!isObject(target)) {
    return target
  }
  
  // 创建代理
  const proxy = new Proxy(
    target,
    baseHandlers
  )
  
  // 设置响应式标识
  def(proxy, '__v_isReactive', true)
  
  return proxy
}

基础代理处理器(baseHandlers)

baseHandlers是Proxy的处理器对象,定义了各种拦截行为。Vue3主要使用以下拦截器:

const baseHandlers = {
  get: createGetter(),
  set: createSetter(),
  has,
  deleteProperty,
  ownKeys
}

get拦截器的实现

get拦截器负责依赖收集和返回值处理。当访问响应式对象的属性时:

  1. 追踪当前正在执行的effect
  2. 如果属性值是对象,递归转换为响应式
  3. 返回属性值
function createGetter() {
  return function get(target, key, receiver) {
    // 依赖收集
    track(target, key)
    
    const res = Reflect.get(target, key, receiver)
    
    // 如果值是对象,递归代理
    if (isObject(res)) {
      return reactive(res)
    }
    
    return res
  }
}

set拦截器的实现

set拦截器负责触发更新:

  1. 检查值是否变化
  2. 设置新值
  3. 触发依赖更新
function createSetter() {
  return function set(target, key, value, receiver) {
    const oldValue = target[key]
    
    // 设置值
    const result = Reflect.set(target, key, value, receiver)
    
    // 只有值变化时才触发更新
    if (hasChanged(value, oldValue)) {
      trigger(target, key)
    }
    
    return result
  }
}

依赖收集与触发机制

track函数实现

track函数将当前正在运行的effect与访问的属性建立关联:

function track(target, key) {
  if (!activeEffect) return
  
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  
  let dep = depsMap.get(key)
  if (!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  
  dep.add(activeEffect)
}

trigger函数实现

trigger函数查找所有依赖该属性的effect并执行:

function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  
  const effects = depsMap.get(key)
  effects && effects.forEach(effect => effect.run())
}

特殊情况的处理

数组的代理处理

数组需要特殊处理以下方法:

  • push/pop/shift/unshift/splice等会改变数组长度的方法
  • includes/indexOf/lastIndexOf等查找方法
const arrayInstrumentations = {}
;['push', 'pop', 'shift', 'unshift', 'splice'].forEach(method => {
  arrayInstrumentations[method] = function(...args) {
    // 暂停依赖收集
    pauseTracking()
    const res = Array.prototype[method].apply(this, args)
    // 恢复依赖收集
    resetTracking()
    return res
  }
})

原始值包装

对于原始值如number/string,Vue提供了ref()来创建响应式引用:

function ref(value) {
  return createRef(value)
}

function createRef(rawValue) {
  return new RefImpl(rawValue)
}

class RefImpl {
  constructor(value) {
    this._value = convert(value)
    this.__v_isRef = true
  }
  
  get value() {
    track(this, 'value')
    return this._value
  }
  
  set value(newVal) {
    if (hasChanged(newVal, this._value)) {
      this._value = convert(newVal)
      trigger(this, 'value')
    }
  }
}

性能优化策略

代理缓存

Vue会缓存已创建的代理,避免重复代理同一对象:

const reactiveMap = new WeakMap()

function reactive(target) {
  // 检查缓存
  const existingProxy = reactiveMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  
  // 创建新代理
  const proxy = createReactiveObject(target)
  reactiveMap.set(target, proxy)
  
  return proxy
}

浅响应式

对于不需要深度响应的场景,Vue提供了shallowReactive:

function shallowReactive(target) {
  return createReactiveObject(
    target,
    shallowReactiveHandlers
  )
}

const shallowReactiveHandlers = {
  get: function(target, key, receiver) {
    // 不进行深度转换
    const res = Reflect.get(target, key, receiver)
    track(target, key)
    return res
  },
  // ...其他拦截器
}

响应式系统的边界情况

不可代理对象的处理

某些特殊对象如Date、RegExp等不能被Proxy代理,Vue会返回原始值:

function canProxy(target) {
  return (
    target instanceof Object &&
    !(target instanceof Date) &&
    !(target instanceof RegExp)
  )
}

循环引用处理

Vue通过代理缓存机制自动处理循环引用:

const obj = { self: null }
obj.self = obj

const proxy = reactive(obj)
console.log(proxy.self === proxy) // true

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌