资讯专栏INFORMATION COLUMN

基于Object.defineProperty实现双向数据绑定

fredshare / 1110人阅读

摘要:双向数据绑定可算是前端领域经久不衰的热词,不管是前端开发还是面试都会有所涉及。因此,中的挺身而出,拯救了中对数组数据处理的不足。有兴趣的朋友请期待笔者的下一篇博客,讨论下用实现双向数据绑定。

双向数据绑定可算是前端领域经久不衰的热词,不管是前端开发还是面试都会有所涉及。而且不同的框架也想尽一切办法去实现这一特性,比如:
Knockout / Backbone --- 发布-订阅模式
Angular --- ‘脏检查’
Vue --- "Object.defineProperty"

那么双向数据绑定到底是什么?没图说个卵,直接上图

简单的说就是在数据UI之间建立双向的通信通道,当用户通过Function改变了数据,那么这个改变也会立即反应到UI上;或者说用户通过UI的操作,那么这些操作也会随之引起对应的数据变动。emmmmmm...没毛病!

既然本文标题是讨论Object.defineProperty,那么笔者就把当前火热的到Boom的国产框架:Vue.js 请出来,然后在了解完她实现双向数据绑定的原理之后,我们着手实现一个抽象派的双向数据绑定。那么那位朋友就说了,什么叫 抽象派 ?我估计吧,可能就是马(Vue)和马骨架的区别吧,TAT...

在介绍Vue的双向数据绑定之前,笔者还想多叨叨几句,如果某一天有人问你:Vue是如何实现双向数据绑定的? 姑且先在这里停顿下,思考下这个问题的答案...................
或许有朋友会脱口而出“数据劫持”,说的没错!的确就是“数据劫持”,但是还不够充分和不够精确。笔者在这里也谈下自己的一点点所见所闻所想:

不够精确:与其说是 数据劫持,更应该说是对数据对象的SetterGetter实现的劫持。

不够充分:为什么说不够充分?是因为 Object.defineProperty 仅仅是实现了对数据的监控,后续实现对UI的重新渲染并不是它做的,所以这里还涉及到 发布-订阅模式(有兴趣的朋友戳这里);过程是,当监控的数据对象被更改后,这个变更会被广播给所有订阅该数据的watcher,然后由该 watcher实现对页面的重新渲染。

下面进入正题,一起了解下Vue实现双向数据绑定的原理,果断上图:

首先,Vue的Compile模块会对Vue的 template 代码进行编译解析并生成一系列的watcher,也可以称之为“更新函数”,它负责把变更后的相关数据重新渲染到指定的地方。举个栗子:

Compile会解析出 v-moel 这个指令并且生成 watcher 并连接数据中的 message 和当前这个Dom对象,一旦收到这个message被变更的通知,watcher就会根据变更对这个Dom进行重新渲染。

当然一个页面或者一个项目中肯定有很多watcher,因此Vue使用了Dep这个对象来存储每一个watcher,当数据发生变更,Observer会调用Dep的notify方法以通知所有订阅了该数据的watcher,让它们醒醒该干活了...

Vue的双向数据绑定也说得差不多了,下面就开始顺着这个思路着手写一个吧,毕竟说得多不如code来得好啊!!!具体的实现效果如下,Let‘s do it

不知道为什么GIF上传不了,所以只能将就用图片了,QAQ....

功能就用文字解释下:
第一个行的 title 0 直接显示的是数据,以便观察;我们可以在输入框中输入任何int, 然后点击“加”可以实现对数值的 +1 操作,同时输入框的数值和 title 也会随之变化;当然,通过输入数值,title也会跟着变化。

首先把Html代码呈上来:





    
    
    
    Object.defineProperty实现双向绑定



    

然后开始一步一步在index.js里写代码吧
1) 首先我们先定义一个数据源

//数据源
let vm = {
    value: 0
}

2) 然后定义一个Dep,用于存储watcher

//用于管理watcher的Dep对象
let Dep = function () {
    this.list = [];
    this.add = function(watcher){
        this.list.push(watcher)
    },
    this.notify = function(newValue){
        this.list.forEach(function (fn) {
            fn(newValue)
        })
    }
};

3) 模拟Compile出来的watchers,该demo涉及到两个地方的重新render,一个是title,另一个是输入框。所以写两个watcher,然后存入Dep

// 模拟compile,通过对Html的解析生成一系列订阅者(watcher)
function renderInput(newValue) {
    let el = document.getElementById("inp");
    if (el) {
        el.value = newValue
    }
}

function renderTitle(newValue) {
    let el = document.getElementById("h1");
    if (el) {
        el.innerHTML = newValue
    }
}

//将解析出来的watcher存入Dep中待用
let dep = new Dep();
dep.add(renderInput);
dep.add(renderTitle)

4) 使用 Object.defineProperty 定义一个Observer

function observer(vm, key, value) {
    Object.defineProperty(vm, key, {
        enumerable: true,
        configurable: true,
        get: function () {
            console.log("Get");
            return value
        },
        set: function (newValue) {
            if (value !== newValue) {
                value = newValue
                console.log("Update")

                //将变动通知给相关的订阅者
                dep.notify(newValue)
            }
        }
    })
}

5) 再将页面使用的两个方法写出来。(Vue使用的是指令对事件进行绑定,但是本文不涉及指令,所以用最原始的方法绑定事件)

//页面引用的方法
function inputChange(ev) {
    let value = Number.parseInt(ev.target.value);
    vm.value = (Number.isNaN(value)) ? 0 : value;
}

function btnAdd() {
    vm.value = vm.value + 1;
}

主要的代码都写好后,下面第一件事就是初始化

//数据初始化方法
function initMVVM(vm) {
    Object.keys(vm).forEach(function (key) {
        observer(vm, key, vm[key])
    })
}

//初始化数据源
initMVVM(vm)

//初始化页面,将数据源渲染到UI
dep.notify(vm.value);

这样一个简单的基于 Object.defineProperty 的双向数据绑定就完成了。看完的朋友有没有对双向数据绑定有了更多的理解了呢?如果没有理解的话,可以将代码复制到本地,然后循着代码再运行下,或许能容易理解。当然这里的代码并不高深,只是从浅层去谈论了双向数据绑定,所以有不足或者表达错误的地方,烦请各位朋友多多指正。

这里是源码,由于放不了动图,所以有兴趣的小伙伴可以拿下来

最后还是补充一句,Object.defineProperty虽然好用,但并不是无懈可击的,它对数组数据的处理并没有想象中的好甚至表现很差,因此Vue团队专门为Vue中的数组类型编写了额外的方法以实现对数组的正确监控
。因此,ES6中的Proxy挺身而出,拯救了ES5 中 Object.defineProperty对数组数据处理的不足。有兴趣的朋友请期待笔者的下一篇博客,讨论下用Proxy实现双向数据绑定。

咱们下期再见!!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95800.html

相关文章

  • 最近老是有兄弟问我,Vue双向绑定的原理,以及简单的原生js写出来实现,我就来一个最简单的双向绑定

    摘要:废话不多说直接看效果图代码很好理解,但是在看代码之前需要知道双向绑定的原理其实就是基于实现的双向绑定官方传送门这里我们用官方的话来说方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。 废话不多说直接看效果图 showImg(https://segmentfault.com/img/bVbiYY5?w=282&h=500); 代码很好理解,但是在看代码之前...

    zhangfaliang 评论0 收藏0
  • 最近老是有兄弟问我,Vue双向绑定的原理,以及简单的原生js写出来实现,我就来一个最简单的双向绑定

    摘要:废话不多说直接看效果图代码很好理解,但是在看代码之前需要知道双向绑定的原理其实就是基于实现的双向绑定官方传送门这里我们用官方的话来说方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。 废话不多说直接看效果图 showImg(https://segmentfault.com/img/bVbiYY5?w=282&h=500); 代码很好理解,但是在看代码之前...

    aristark 评论0 收藏0
  • JavaScript 进阶之深入理解数据双向绑定

    摘要:当我们的视图和数据任何一方发生变化的时候,我们希望能够通知对方也更新,这就是所谓的数据双向绑定。返回值返回传入函数的对象,即第一个参数该方法重点是描述,对象里目前存在的属性描述符有两种主要形式数据描述符和存取描述符。 前言 谈起当前前端最热门的 js 框架,必少不了 Vue、React、Angular,对于大多数人来说,我们更多的是在使用框架,对于框架解决痛点背后使用的基本原理往往关注...

    sarva 评论0 收藏0
  • vue.js源码 - 剖析observer,dep,watch三者关系 如何具体的实现数据双向绑定

    摘要:双向数据绑定的核心和基础是其内部真正参与数据双向绑定流程的主要有和基于和发布者订阅者模式,最终实现数据的双向绑定。在这里把双向数据绑定分为两个流程收集依赖流程依赖收集会经过以上流程,最终数组中存放列表,数组中存放列表。 Vue双向数据绑定的核心和基础api是Object.defineProperty,其内部真正参与数据双向绑定流程的主要有Obderver、Dep和Watcher,基于d...

    mo0n1andin 评论0 收藏0

发表评论

0条评论

fredshare

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<