欢迎光临
我们一直在努力

Vue 双向绑定原理剖析

Vue 双向绑定原理剖析(Vue 3 时代完整版,2026视角)

Vue 的“双向绑定”其实是两个方向的单向流动巧妙组合的结果:

  • 数据 → 视图:响应式系统(Reactivity)自动更新 DOM(Vue 2 用 Object.defineProperty,Vue 3 用 Proxy + Reflect)
  • 视图 → 数据:用户交互(input、change 等事件)触发事件监听 → 更新数据
  • Vue 3 中 v-model 是语法糖,底层拆分成 value(或 modelValue) + onInput(或 update:modelValue)。

    下面按层级从底层到表层完整拆解。

    1. Vue 3 响应式系统核心(数据 → 视图方向)

    Vue 3 放弃了 Object.defineProperty,转用 Proxy(性能更好,能劫持更多操作,如数组长度变化、in 操作、delete 等)。

    核心 API:

    • reactive(obj) → 返回 Proxy 代理对象
    • ref(value) → 返回 { value: xxx } 的 Ref 对象(内部也是 Proxy 或 getter/setter)
    • computed()、watch() 等依赖这个系统

    Proxy 实现响应式的大致原理(简化版伪代码):

    function reactive(raw) {
    return new Proxy(raw, {
    get(target, key, receiver) {
    // 依赖收集:track
    track(target, key); // 把当前 effect 收集到这个 key 的 deps 中
    const result = Reflect.get(target, key, receiver);
    // 如果是对象,继续代理(深度响应式)
    if (isObject(result)) return reactive(result);
    return result;
    },

    set(target, key, value, receiver) {
    const oldValue = target[key];
    const result = Reflect.set(target, key, value, receiver);
    // 值真的变了才触发
    if (oldValue !== value) {
    trigger(target, key); // 通知所有依赖这个 key 的 effect 重新运行
    }
    return result;
    },

    // deleteProperty、has、ownKeys 等也需要处理
    });
    }

    track / trigger 机制(依赖收集与派发更新):

    • 使用 WeakMap<target, Map<key, Set>> 结构存储依赖关系
    • effect(fn) 执行时会开启 activeEffect,get 时收集,set 时触发所有收集到的 effect

    这就是为什么 ref.value++、reactiveObj.count++ 会自动更新视图的原因。

    2. v-model 在普通 input 上的拆解(最基础形式)

    <input v-model="message" />

    编译后等价于:

    <input
    :value="message"
    @input="message = $event.target.value"
    />

    • :value → 数据 → 视图(响应式触发 set → trigger → 重新渲染 value)
    • @input → 视图 → 数据(用户输入 → 赋值给 message → 触发 set → 视图更新)

    这就是最原始的双向绑定实现。

    3. 组件上的 v-model(Vue 3.3+ 推荐方式:defineModel)

    Vue 3.4 后 defineModel 正式成为宏(macro),极大简化写法。

    子组件写法对比:

    Vue 3.3 以前(手动):

    // 子组件
    defineProps(['modelValue']);
    defineEmits(['update:modelValue']);

    // 模板
    <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />

    Vue 3.4+(defineModel 宏,推荐):

    // 子组件(一行搞定)
    const model = defineModel(); // 返回一个 ref,默认绑定 modelValue + update:modelValue

    // 或者带选项
    const model = defineModel({ required: true, default: '' });

    // 模板(和普通 ref 一样用)
    <input vmodel="model" />

    defineModel 内部等价实现(官方简化版):

    function defineModel(options) {
    const props = useProps();
    const emit = useEmit();

    const local = ref(props.modelValue);

    watch(() => props.modelValue, val => {
    local.value = val; // 父 → 子 同步
    }, { deep: true });

    watch(local, val => {
    emit('update:modelValue', val); // 子 → 父 通知
    }, { deep: true });

    return local;
    }

    一句话:defineModel 就是一个自动创建的 ref + 双向 watch + 事件桥接。

    4. 自定义修饰符的 v-model(.trim / .number / .lazy)

    Vue 3 支持在组件上自定义修饰符:

    <MyInput v-model.trim="search" />

    组件内可以通过 defineModel({ modifier: true }) 或手动处理。

    5. 多 v-model(多个字段绑定,Vue 3 特性)

    <UserForm v-model:name="user.name" v-model:age="user.age" />

    子组件:

    const name = defineModel('name');
    const age = defineModel('age');

    6. Vue 2 vs Vue 3 双向绑定对比(面试高频)

    维度Vue 2Vue 3优劣对比
    响应式核心 Object.defineProperty Proxy + Reflect Proxy 更全面、更高效(数组友好)
    数组响应式 需要 Vue.set / this.$set 天然支持(Proxy 劫持 length 等) Vue 3 完胜
    v-model 在组件上 value + input(自定义 prop/event) modelValue + update:modelValue 更规范,defineModel 极大简化
    性能 递归遍历所有属性 惰性代理 + 只代理已访问路径 Vue 3 更好(尤其大对象)
    新增/删除属性 不响应式(需 Vue.set) 响应式(Proxy set/has/delete) Vue 3 完胜

    7. 总结口诀(背下来面试稳)

    • Vue 3 双向绑定 = Proxy 响应式(数据→视图) + 事件监听(视图→数据) + v-model 语法糖
    • 普通标签::value + @input
    • 组件:modelValue + update:modelValue(或 defineModel 一行实现)
    • 核心优势:Proxy 让数组、动态新增属性天然响应式,告别 Vue.set
    • 2026 最佳实践:优先用 defineModel,配合 <script setup>,代码最简洁

    如果你想看手写一个 mini-Vue 双向绑定(Proxy + 发布订阅 + 简单 diff),或者深挖 effect、track、trigger 的源码级细节,或者组件库中 v-model 的高级玩法(如 .sync 迁移、自定义修饰符),直接说,我继续拆解!

    赞(0)
    未经允许不得转载:171主机测评 » Vue 双向绑定原理剖析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址