Vue 的响应式系统原理

Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。

当你把javascript对象传给vue实例的data属性的时候,Vue 将遍历此对象所有的属性,通过 Object.defineProperty 来给每个属性都添加 gettersetter.

而每个组件实例又都有一个watcher对象,它会将组件渲染过程中的属性渲染为【依赖】,当数据发生变化的时候,会触发setter,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

一、Object.defineProperty

Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。

Object.defineProperty(obj, prop, descriptor)

参数

  • obj 需要定义属性的对象。
  • prop 需被定义或修改的属性名。
  • descriptor 需被定义或修改的属性的描述符。
Object.defineProperty(obj, "key", {
  enumerable: false,    // 不可枚举
  configurable: false,  // 不可更改
  writable: false,      // 不可被赋值运算符改变
  value: "static"
})

// 创建属性
var o = {};

Object.defineProperty(o, 'a', {
    enumerable: true,
    configurable: true,
    writable: true,
    value: 17
});

// 对象o拥有了属性a,值为17
var bValue = 38;
Object.defineProperty(o, 'b', {
  get: function() { return bValue; },
  set: function(newValue) { bValue = newValue; },
  enumerable: true,
  configurable: true
});
o.b; // 38
  • Writable 属性
var o = {}; // 创建一个新对象

Object.defineProperty(o, "a", { value : 37,
                                writable : false });

console.log(o.a); // 打印 37
o.a = 25; // 没有错误抛出(在严格模式下会抛出,即使之前已经有相同的值)
console.log(o.a); // 打印 37, 赋值不起作用。
  • Enumerable 特性
var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable:true });
Object.defineProperty(o, "b", { value : 2, enumerable:false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true

for (var i in o) {    
  console.log(i);  
}
// 打印 'a' 和 'd' (in undefined order)

Object.keys(o); // ["a", "d"]

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
  • Configurable 特性

configurable 特性表示对象的属性是否可以被删除,以及除 writable 特性外的其他特性是否可以被修改。

Object.defineProperty(person,'a',{
    configurable:false,//为false的时候不允许修改默认属性了
})
===============================
# 改为false之后再试试修改其他属性
Object.defineProperty(person,'a',{
    configurable:true,
    enumerable:true,
    writable:true,
    value:1
})
//woa,控制台直接报错了!连想把false值改回true都不行!也就是说,这个改动是一次性了!
//也就是说,你可以使用Object.defineProperty()方法无限修改同一个属性,但是当把configurable改为false之后就有限制了

1 、首先定义一个cb函数,这个函数用来模拟试图更新。

// 
function cb (val) {
    /* 渲染视图 */
    console.log("视图更新啦~");
}

2、 定义一个defineReactive ,这个方法通过 Object.defineProperty 来实现对对象的「响应式」化,入参是一个 obj(需要绑定的对象)key(obj的某一个属性)val(具体的值)

function defineReactive(obj, key, val){
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function(){
            return val;
        },
        set:function(newVal) {
            if (newVal === val) return;
            cb(newVal);
        }
    })
}
// 当一个对象,赋值给Vue实力的data选项的时候,就会触发该操作,对数据进行响应化处理

3、 当然这是不够的,我们需要在上面再封装一层 observer 。这个函数传入一个 value(需要「响应式」化的对象),通过遍历所有属性的方式对该对象的每一个属性都通过 defineReactive 处理

function observer(val) 
{
    if(!val || typeof val != 'object') {
        return ;
    }
    Object.keys(val).forEach((key)=> {
        defineReactive(val,key, val[key]);
    })
}

4、 在 Vue 的构造函数中,对 options 的 data 进行处理

class Vue {
    /* Vue构造类 */
    constructor(options) {
        this._data = options.data;
        observer(this._data);
    }
}

5、 这样我们只要 new 一个 Vue 对象,就会将 data 中的数据进行「响应式」化。如果我们对 data 的属性进行下面的操作,就会触发 cb 方法更新视图。

本文固定链接: http://www.js-code.com/vue-js/vue-js_26850.html