Vue.js双向绑定内核—Object.defineProperty解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33612918/article/details/79716261

前言

最近Vue.js特别火,对于它的mvvm的特性,笔者感到特别好奇,于是通过原作者的话了解到,Vue使用了JavaScript的 Object.defineProperty函数来实现数据的双向绑定。
这里,我将记录该方法的学习成果,也算是一个总结吧  ̄へ ̄

过程

描述

首先看 MDN 上对该方法的描述:

Object.defineProperty(obj, prop, descriptor)

    obj
        要在其上定义属性的对象。
    prop
        要定义或修改的属性的名称。
    descriptor
        将被定义或修改的属性描述符。 

    返回值
        被传递给函数的对象。

使用方法

  1. 创建属性
var person = {}

// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(person,'age',{
    configurable:false,//configurable特性表示对象的属性是否可以被删除,以及除writable特性外的其他特性是否可以被修改。
    enumerable:false,//对象属性是否可通过for-in循环,false为不可循环,默认值为true
    writable:false,//对象属性是否可修改,false为不可修改,默认值为true
    value:'17' //对象属性的默认值,默认值为undefined
});

//writable
person.age="71";
console.log(person);//17,不可修改value,而且在严格模式下会抛出错误

//enumerable
person.name = "Allen"; 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true
for(var i in person){
    console.log(person[i]) //无结果,不可循环
}

//configurable
delete person.age
console.log(person.age)//17,不可删除

Object.defineProperty(person,'age',{
    configurable:true //不可修改,将抛出错误
});

2.一般的 Setters 和 Getters

function Factory(){
    var value = null;
    var factory = [];

    Object.defineProperty(this, 'temperature',{
        get: () => {
            console.log('get!');
        },
        set: val => {
            value = val;
            factory.push(val);
        }
    });

    this.getFactory = () => factory;
}

var f = new Factory();
f.temperature;                  //打印 get!
f.temperature = 22;
f.temperature = 33;
console.log(f.getFactory());    //打印 [22,33]

可以看到,当数据获取或者改变时,会调用函数的getset方法,如此,再辅以视图层的操作,便实现了数据的双向绑定

分析

进一步,我们还没有满足:那Vue是如何使用这个方法做到的呢?
为了解决这个疑问,我们不得不翻开Vue的源代码一览究竟
翻开Vue源代码,可以看到里面普遍使用了Object.defineProperty方法,但是里面拓展了Property来避开Object.defineProperty,其实原理是一样的。如下面Vue的源码片段所示

// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
    initProps(Sub)
}
if (Sub.options.computed) {
    initComputed(Sub)
}

对于Vue来说,它并不是单纯地通过数据和dom节点的绑定来绑定数据,它在网页dom和accessor之间会有两层,一层是Wacher,一层是Directive。如下图所示
这里写图片描述
如果我们通过代码修改了a.b的值,那么set函数就会通知Watcher,再由Watcher通知Directive修改Dom里a.b的值

结语

那么,如果我们要来实现Vue的功能,代码我们该怎么写呢?这里有一个小小的例子供你参考
html页面:

<div id="app">
    Hello World!
</div>

主体部分

//模仿Vue操作dom的语法
//我们自己创建一个Vue函数
const Vue = function({el, data={message:''}}){
    //绑定 get和set方法
    Object.defineProperty(this,'message',{
        get: () => {
            return document.getElementById(el).innerHTML
        },
        set: val => {
            document.getElementById(el).innerHTML = val
        }
    })
}

const v = new Vue({
    el: 'app',
    data: {
        message: 'Wooooooooooo!'
    }
})
v.message                   //返回 Wooooooooooo! 字符串
v.message = 'ohhhhhhhhhhh!' //将div内的内容变为 ohhhhhhhhhhh!

这样,我们就能进行数据交互了!
当然,Vue源码会比我们的练习代码复杂得多,学无止境,衣带渐宽终不悔,为伊消得人憔悴

猜你喜欢

转载自blog.csdn.net/qq_33612918/article/details/79716261