当前位置: 首页 > news >正文

vue3数据双向绑定解析

Vue 3 的双向绑定原理主要基于 Proxy 和 Reflect,核心源码在 reactivity 模块中。

1. 核心模块:reactivity

reactivity 模块负责响应式数据的实现,主要包括以下几个文件:

  • reactive.ts:处理对象和数组的响应式。
  • ref.ts:处理基本类型的响应式。
  • effect.ts:管理副作用和依赖追踪。

2. reactive.ts

reactive.ts 中的 reactive 函数将普通对象转换为响应式对象,核心代码如下:

export function reactive<T extends object>(target: T): T {
  if (target && (target as any).__v_isReadonly) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers
  )
}

function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>
) {
  if (!isObject(target)) {
    return target
  }
  const proxy = new Proxy(target, baseHandlers)
  return proxy
}

reactive 函数通过 Proxy 包装目标对象,mutableHandlers 定义了 get 和 set 等拦截操作。

3. mutableHandlers

mutableHandlers 定义了 Proxy 的拦截行为,核心代码如下:

export const mutableHandlers: ProxyHandler<object> = {
  get(target: Target, key: string | symbol, receiver: object) {
    const res = Reflect.get(target, key, receiver)
    track(target, key)
    return isObject(res) ? reactive(res) : res
  },
  set(target: Target, key: string | symbol, value: any, receiver: object) {
    const oldValue = (target as any)[key]
    const result = Reflect.set(target, key, value, receiver)
    if (hasChanged(value, oldValue)) {
      trigger(target, key)
    }
    return result
  }
}
  • get:在访问属性时调用 track 追踪依赖。
  • set:在修改属性时调用 trigger 触发更新。

4. effect.ts

effect.ts 负责管理副作用和依赖追踪,核心代码如下:

export function effect<T = any>(
  fn: () => T,
  options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect<T> {
  const effect = createReactiveEffect(fn, options)
  if (!options.lazy) {
    effect()
  }
  return effect
}

function createReactiveEffect<T = any>(
  fn: () => T,
  options: ReactiveEffectOptions
): ReactiveEffect<T> {
  const effect = function reactiveEffect(): unknown {
    if (!effect.active) {
      return options.scheduler ? undefined : fn()
    }
    if (!effectStack.includes(effect)) {
      cleanup(effect)
      try {
        enableTracking()
        effectStack.push(effect)
        activeEffect = effect
        return fn()
      } finally {
        effectStack.pop()
        resetTracking()
        activeEffect = effectStack[effectStack.length - 1]
      }
    }
  } as ReactiveEffect
  effect.id = uid++
  effect._isEffect = true
  effect.active = true
  effect.raw = fn
  effect.deps = []
  effect.options = options
  return effect
}

effect 函数创建并执行副作用函数,track 和 trigger 分别用于依赖追踪和触发更新。

5. track 和 trigger

track 和 trigger 是依赖追踪和更新的核心:

export function track(target: object, key: unknown) {
  if (!shouldTrack || activeEffect === undefined) {
    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()))
  }
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
  }
}

export function trigger(
  target: object,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  const depsMap = targetMap.get(target)
  if (!depsMap) {
    return
  }
  const effects = new Set<ReactiveEffect>()
  const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
    if (effectsToAdd) {
      effectsToAdd.forEach(effect => {
        if (effect !== activeEffect || effect.allowRecurse) {
          effects.add(effect)
        }
      })
    }
  }
  if (key !== void 0) {
    add(depsMap.get(key))
  }
  const run = (effect: ReactiveEffect) => {
    if (effect.options.scheduler) {
      effect.options.scheduler(effect)
    } else {
      effect()
    }
  }
  effects.forEach(run)
}
  • track:将当前副作用函数添加到依赖集合。
  • trigger:遍历依赖集合并执行副作用函数。

Vue 3 的双向绑定通过 Proxy 拦截对象操作,结合 track 和 trigger 实现依赖追踪和更新触发。effect 函数管理副作用,确保数据变化时视图自动更新。

相关文章:

  • Unity3D 着色器优化(Shader Optimization)
  • 机器人基础知识
  • 如何设置GET请求的参数?
  • SAP SD学习笔记31 - 销售BOM
  • Java基础入门流程控制全解析:分支、循环与随机数实战
  • 部署MuseTalk时, MMCV问题解决方案
  • 【MyBatis Plus 逻辑删除详解】
  • DICOM开发者常用DICOM开源库详解
  • 推理大模型时代,TextIn ParseX助力出版业知识资产重构
  • 全球领先的光学方案设计公司:倚光科技
  • Android Spinner总结
  • 23、vue3+vite配置环境变量实现开发、测试、生产的区分
  • LLM最新的模型微调技术有哪些
  • c语言闯算法--常用技巧
  • LINUX 指令大全
  • 养生,点亮健康生活
  • 监听新打开窗口的关闭情况和iframe内系统的url改变情况
  • 如何用“几何产品规范”让齿轮测量更精准?
  • 如何选择适合您智能家居解决方案的通信协议?
  • Spring Boot 项目部署启动异常问题分析与解决​:主类缺失与依赖冲突的分析
  • 虚假认定实质性重组、高估不良债权价值,原中国华融资产重庆分公司被罚180万元
  • 上海启动万兆光网试点建设,助力“模速空间”跑出发展加速度
  • 大四本科生已发14篇SCI论文?学校工作人员:已记录汇报
  • 成都公积金新政征求意见:购买保障性住房最高贷款额度上浮50%
  • 杭温高铁、沪苏湖高铁明起推出定期票和计次票,不限车次执行优惠折扣
  • 中科院院士魏辅文已卸任江西农业大学校长