vue源码(1)一文读懂vue2和vue3的双向绑定原理

102 阅读4分钟

Vue响应式数据的原理设计思想

十字真理:==**数据劫持 + 发布-订阅模式== vue双向数据绑定是 通过 数据劫持 并结合 发布-订阅模式 的方法来实现的。 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变。

双向绑定流程

  1. new Vue()首先执行初始化,对data执行响应化处理,这个过程发生Observer中【劫持监听所有数据】

  2. 同时对模板执行编译,找到其中动态绑定的数据,从data中获取并初始化视图,这个过程发生在Compile中【解析模板中的指令】

  3. 同时定义⼀个更新函数和Watcher,将来对应数据变化时Watcher会调用更新函数

  4. 由于data的某个key在⼀个视图中可能出现多次,所以每个key都需要⼀个管家Dep来管理多个Watcher

  5. 将来data中数据⼀旦发生变化,会首先找到对应的Dep,通知所有Watcher执行更新函数

    图解:来张网图

    3f2aa4e65d7390d0cdf4315258d97373

object.defineProperty和proxy

vue实现方式优点缺点
vue2object.defineProperty支持ie91.不能拦截数组,2.必须挨个遍历对象的属性才能进行拦截3.必须递归遍历对象的属性,防止对象的属性还是对象
vue3proxy1,直接监听对象而非属性,2.直接监听数组的变化3.拦截的方式有很多种(有13种,set,get,has)4.Proxy返回一个新对象,可以操作新对象达到目的不支持ie

虽然Proxy和Object.defineProperty一样都需要递归拦截属性的get,set,但是Proxy只在调用时递归,Object.defineProperty在一开始就递归,所以Proxy性能更高。

###vue2实现双向绑定

1174b63e-32e3-405e-82a6-ff3547531e5d

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue2双向数据绑定</title>
</head>
<body>
<div style="padding: 80px;width: 300px;margin: auto">
    <h3>vue2双向数据绑定实现:</h3>
    <hr>
    <p>输入:<input type="text" id="ipt" /></p>
    <p>结果:<span id="text"></span></p>
</div>

<script>
    // 页面加载获取对应的两个节点
    window.onload = function(){
        var ipt = document.getElementById('ipt');
        var text = document.getElementById('text');
        // 声明一个空对象,用于数据双向绑定
        var obj = {};
        // 页面加载--绑定初始值,并监听--等着被修改
        reactFun(obj,'asf')
        function reactFun(obj,val){
            // 重新定义该空对象,实现该对象的属性的监听
            //Object.defineProperty它可以用来给一个对象定义一个属性或者修改一个现有的属性,并且返回这个对象。
            Object.defineProperty(obj,'name',{
                configurable: true,//configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除
                enumerable: true,//对象是否可枚举
                get: function(){
                    return val;
                },
                set: function(newVal){
                    console.log('set:');
                    // 重新赋值到上层作用域
                    val = newVal;
                }
            });
        }

        // 对输入框绑定输入事件
        ipt.onkeyup = function(event){
            // 当输入变化时,重新赋值给obj.name
            // 修改对象的值
            obj.name = event.target.value;
            // 取得对象的值,赋值给页面
            text.innerHTML = obj.name;
        }
    }
</script>
</body>
</html>

vue3实现数据双向绑定

Proxy是ES6引入的一个新特性,它允许你创建一个代理对象,用于拦截对目标对象的访问。通过使用Proxy,你可以拦截目标对象上的各种操作,比如属性访问、属性赋值、函数调用等,并在这些操作发生时执行自定义的逻辑。

Vue3中,Proxy被用于劫持组件实例,以实现响应式数据的跟踪和更新。当你在Vue组件中声明一个响应式的数据属性时,Vue内部会使用Proxy来追踪该属性的变化。这样,当属性的值发生变化时,Vue能够自动检测到这个变化,并更新相关的视图。

拦截的方式有很多种 get set has apply

0339a950-83bb-4ce4-8e02-73eb6d113165

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue3双向数据绑定</title>
</head>
<body>
<div>
    <h3>vue3双向数据绑定实现:</h3>
    <hr>
    <p>输入:<input type="text" id="ipt" /></p>
    <p>结果:<span id="text"></span></p>
</div>

<script>
    // 页面加载获取对应的两个节点
    window.onload = function(){
        var ipt = document.getElementById('ipt');
        var text = document.getElementById('text');
        // 声明一个空对象,用于数据双向绑定
        var obj = {};
        var obj1 = new Proxy(obj, {
            // target就是第一个参数obj, receive就是返回的obj(返回的proxy对象)
            get: function (target, key, receive) {
                // 返回该属性值
                return target[key];
            },
            set: function (target, key, newVal, receive) {
                // 执行赋值操作
                target[key] = newVal;

            }
        })
        ipt.onkeyup = function(event){
            obj1[0] = event.target.value;
            text.innerHTML = obj1[0]
        }
    }
</script>
</body>
</html>

有问题请联系我哦 @一路向北xwq

image-20231006155957129