数据双向绑定原理

本文深入探讨了MVVM模式下的双向数据绑定原理,通过数据劫持结合发布者-订阅者模式,利用Object.defineProperty()实现数据变化时视图的自动更新。详细介绍了Observer、Compile和Watcher的作用,以及ECMAScript中数据属性和访问器属性的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

所谓双向数据绑定,无非就是数据层和视图层中的数据同步,在写入数据时视图层实时的跟着更新,具体描述:
实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,出发相应的监听回调。就必须要实现以下几点:

  1. 实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者。
  2. 实现一个指令解析器Compile,对每隔元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数。
  3. 实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并受到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
  4. mvvm入口函数,整合以上三者
    在这里插入图片描述
    个人理解就是getter函数里面执行的任务就是watcher订阅者,而setter函数执行的任务就是发布者;解释:
    ECMAScript中有两种属性:数据属性和访问器属性,数据属性一般用于存储数据数值,访问器属性对应的是set/get操作,不能直接存储数据值,每种属性下面又都含有四个特性,下面介绍一下:
数据属性:
  1. [[Configurable]] : 表示能否通过delete将属性删除,能否把属性修改为访问器属性,默认为false。当把属性Configurable设置为false后,该属性不能通过delete删除,并且也无法再将该属性的Configurable设置回true
  2. [[Enumerable]] : 表示属性可否被枚举(即是否可以通过for in循环返回),默认false
  3. [[Writable]] : 表示属性是否可写(即是否可以修改属性的值),默认false
  4. [[Value]] :该属性的数据值,默认是undefined
访问器属性
  1. [[Configurable]] :表示能否通过delete将属性删除,能否把属性修改为数据属性,默认为false。当把属性Configurable设置为false后,该属性不能通过delete删除,并且也无法再将该属性的Configurable设置回true
  2. [[Enumerable]] :表示属性可否被枚举(即是否可以通过for in循环返回),默认false
  3. [[Get]] :读取属性时调用的函数,默认为undefined
  4. [[Set]] :写入属性时调用的函数,默认是undefined

在修改属性的特性或者定义访问属性的时候,需要借助ECMAScript5中的一个方法:
Object.defineProperty(),这个方法接收三个参数:属性所在对象,属性的名字,描述符合对象;为对象定义多个属性的话,就用函数的复数写法:Object.defineProperties();
那么通过这个ES5的方法就可以直接说明双向绑定的原理:

<input type="text" id="inp" />
<div id="box"></div>
<script>
let obj = {};
let oInp = document.getElementById('inp');
let oBox = document.getElementById('box');
Object.defineProperty(obj, 'name', {
    configurable: true,
    enumerable: true,
    get: function() {
        console.log(111)
        return val;
    },
    set: function(newVal) {
        oInp.value = newVal;
        oBox.innerHTML = newVal;
    }
});
oInp.addEventListener('input', function(e) {
    obj.name = e.target.value;
});
obj.name = '苏日俪格';

那么实现数据双向绑定的核心就是利用为每一个属性都创建了订阅者的实例对象,以便观察,getter函数里面返回一个value值,在setter函数中写入修改后的值并调用update方法更新视图的数据值,Configurable和Enumerable这两个特性描述符默认是true,因此不用写

function defineReactive (obj, key, val) {
    var dep = new Dep();  //这是一个构造函数  其原型是为属性添加订阅者
    Object.defineProperty(obj, key, {
       get: function() {
            if(Dep.target) {
                dep.addSub(Dep.target);  //添加订阅者到Dep实例对象
            }
            return val;  // 返回监听到的value值
        },
        set: function (newVal) {
            if(newVal === val) return;
            val = newVal;  // 写入新的value值
            dep.notify();  // 作为发布者发出通知 然后dep会迭代调用各自的update方法来更新视图
        }
    });
}
function observe(obj, vm) {
     Object.keys(obj).forEach(function(key) {
           defineReactive(vm, key, obj[key]);
     });
}

原文地址:https://www.jianshu.com/p/23180880d3aa

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值