vue双向绑定原理和Angular的双向绑定原理

2,500 阅读2分钟

Vue双向绑定原理

在vue2中,采用数据劫持结合订阅者-发布者模式的方式,通过objec.defineproperty()来劫持各个属性的getter与setter。在数据变化时发布消息给订阅者,触发相应的监听回调。

<body>
    姓名:<span id="spanName"></span>
    <br>
    <input type="text" id="inpName">
</body>
    <!-- es5的方法,用Object.defineProperty() -->
    <script>
        let obj={
            name:''
        }
        let newObj=JSON.parse(JSON.stringify(obj));
        Object.defineProperty(obj,'name',{
            get(){
                return newObj.name;
            },
            set(val){
                if(val===newObj.name)return
                newObj.name=val;
                observer();
            }
        });
        function observer(){
            spanName.innerHTML=obj.name;
            inpName.value=obj.name;
        }
        inpName.oninput=function(){
            obj.name=this.value;
        }
    </script>

在vue3使用proxy,优点:

1.不用对原数组克隆
2.不需要给对象每个属性设置监听
<body>
    姓名:<span id="spanName"></span>
    <br>
    <input type="text" id="inpName">
</body>
<!-- es6的方法,用proxy 
	优点:1.不用对原数组克隆
		 2.不需要给对象每个属性设置监听
-->
	<script>
        let obj={};
        obj=new Proxy(obj,{
            get(target,prop){
                return target[prop];
            },
            set(target,prop,value){
                target[prop]=value;
                observer();
            }
        });
        function observer(){
            spanName.innerHTML=obj.name;
            inpName.value=obj.name;
        }
        inpName.oninput=function(){
            obj.name=this.value;
        }
    </script>

Angular双向绑定原理

双向绑定顾名思义是两个过程,一个是将scope属性值绑定到HTML结构中,当scope属性值发生变化的时候界面也发生变化;另一个是,当用户在界面上进行操作,例如点击、输入、选择时,自动触发scope属性的变化(界面也可能跟着变)。而脏检查的作用是“在当scope属性值发生变化的时候促使界面发生变化”。

其中双向数据绑定的实现使用了scope 变 量的脏值检测 , 使用scope变量的脏值检测,使用scope变量的脏值检测,使用scope.watch (视图到模型),watch(视图到模型),scope.apply ( 模型到视图) 检测,内部调用的都是digest,当然也可以直接调用apply(模型到视图)检测,内部调用的都是digest,也可以直接调用scope.$digest进行脏检查。值得注意的是当数据变化十分频繁时,脏检测对浏览器性能的消耗将会很大,官方注明的最大检测脏值为2000个数据

angular根本不监听数据的变动,而是在恰当的时机从rootScope开始遍历所有scope,检查它们上面的属性值是否有变化,如果有变化,就用一个变量dirty记录为true,再次进行遍历,如此往复,直到某一个遍历完成时,这些scope的属性值都没有变化时,结束遍历。由于使用了一个dirty变量作为记录,因此被称为脏检查机制。

当一个作用域创建的时候,angular会去解析模板中当前作用域下的模板结构,并且自动将那些插值(如{{text}})或调用(如ng-click="update")找出来,并利用$watch建立绑定,它的回调函数用于决定如果新值和旧值不同时(或相同时)要干什么事。

当使用watch绑定了要检查的属性之后,当这个属性发生变化,就会执行回调函数。但是前面已经说过了,angular里面没有监听这么一说,那么它怎么会被回调呢?它没有用object的setter机制,而是脏检查机制。脏检查的核心,就是digest循环。当用户执行了某些操作之后,angular内部会调用digest(),最终导致界面重新渲染。那么它究竟是怎么一回事呢?

调用watch之后,对应的信息被绑定到angular内部的一个watchers中,它是一个队列(数组),而当digest被触发时,angular就会去遍历这个数组,并且用一个dirty变量记录?watchers里面记录的那些scope属性是否有变化,当有变化的时候,dirty被设置为true,在digest执行结束的时候,它会再检查dirty,如果dirty为true,它会再调用自己,直到dirty为true。但是为了防止死循环,angular规定,当递归发生了10次或以上时,直接抛出一个错误,并跳出循环。