Vue中的视图更新原理1

在Vue中我们都知道是数据去驱动视图的变化。那么数据是如何驱动视图的更新的,在vue中主要使用了Object.definedProperty()这个方法。
一、1.Object.definedProperty(obj,prop,desc,)是在一个对象上定义一个新的属性或者修改现有的属性。
它接收3个参数,第一个为对象,第二个为属性,第三个为属性的描述对象。
一般情况下我们给对象定义属性是直接进行赋值操作,那么定义的属性可以修改也可以删除,那么如果我们相对其进行更加精准的操作,就需要使用这个方法。

2.在js中一共有3种类型的属性:

  1. 命名数据属性:拥有一个确定的值的属性。这也是最常见的属性
    2.命名访问器属性:通过getter和setter进行读取和赋值的属性
    3.内部属性:由JavaScript引擎内部使用的属性,不能通过JavaScript代码直接访问到,不过可以通过一 些方法间接的读取和设置。比如,每个对象都有一个内部属性[[Prototype]],你不能直接访问这个属性,但可以通过Object.getPrototypeOf()方法间接的读取到它的值。虽然内部属性通常用一个双吕括号包围的名称来表示,但实际上这并不是它们的名字,它们是一种抽象操作,是不可见的,根本没有上面两种属性有的那种字符串类型的属性。
      var obj = {};
      obj.a = "a";
      obj["b"]="b"
      console.log(obj);
      //直接通过字面量进行赋值操作
     var obj = {};
      Object.defineProperty(obj, "a", {
        value: "5",
        writable:false,//writable代表属性值是否可以改变
        enumerable:true//属性是否可以枚举
      });
      obj.a="6666" //上面为false所以它是变不了。
      console.log(obj); //这里打印依旧为5
      
      //用 for in 和 Object.keys语法去枚举属性
      for( x in obj){
          console.log('-------',x) //上面enumerable为true所以可以枚举。此处打印为属性a
      }
     let prop= Object.keys(obj)
     console.log(prop) //上面enumerable为true所以可以枚举。此处打印为属性a

属性描述对象的属性有6个:
1. value:“属性值”,
2. writable:“属性值是否可变”,
3. enumerable:“属性是否可以枚举”
4. configurable:“是否可配置” :configurable: false 时,不能删除当前属性,且不能重新配置当 前属性的描述符(有一个小小的意外:可以把writable的状态由true改为false,但是无法由false改为true),但是在writable: true的情况下,可以改变value的值,configurable: true时,可以删除当前属性,可以配置当前属性所有描述符。
5. get()函数:“取值函数”,一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined。
6. set()函数:“存值函数”,一个给属性提供setter的方法,如果没有setter则为undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined。

1.使用get,set方法
      var obj = {};
      let temp = null;
      Object.defineProperty(obj, "a", {
        get: function () {
          return temp;
        },
        set: function (val) {
          temp = val;
        },
      });
     obj.temp="4111"  //调用了set函数
     console.log(obj.tmep);  //调用了get函数 //此处打印undefinded
     console.log(obj); //打印 {tmep:"4111"}
2. 使用 get set方法
      var obj = { _b: "yz" };
      let temp = null;
      Object.defineProperty(obj, "_b", {
        get: function () {
          return this._b; //这里直接返回obj对象的属性b
        },
        set: function (val) {
          this._b = val; //我们传递的新值赋值给b属性
        },
      });
      obj.b = "新值";
      console.log(obj); //{b:"新值"}
  var obj = {};
      obj.a = "a";
      //1. 直接通过字面量进行赋值操作等于下面
      Object.defineProperty(obj, "a", {
        value: "a",
        writable: true,
        enumerable: true,
        configurable: true,
      });
      //2. 我们直接定义新的属性通过这个方法
       Object.defineProperty(obj,"b",{
           value:"b"
       })
       //属性的配置描述符,如果我们不写,其他3个默认值为false

二 、大体对这个方法有了了解,那么我们继续介绍Vue中如何使用这个方法进行数据驱动视图的渲染的。


1.给对象类型数据添加新的属性

    function defindObj(obj, key, val) {
        Object.defineProperty(obj, key, {
          enumerable: true, //可以枚举
          configurable: true, //可配置
          get: function () {
            return val;
          },
          set: function (newVal) {
              console.log("set",val);  //打印set yz
              val = val + newVal;
          },
        });
      }
      let obj = { name: "yz", age: 18 };
      Object.keys(obj).forEach(key => {
        defindObj(obj,key,obj[key])
      });
      obj.name="重新设置name"   //触发set函数
      console.log(obj);  //{name:"yz 重新设置name" ,age:18}

      obj.unknow="5555"  //添加一个新的属性不会触发set函数,
      //这个也就验证了vue中定义对象的时候我们如果给定义好的对象再添加属性,它就监听不到属性值得变化。
2.给数组类型添加新的属性

    function defineArr(arr, key, val) {
        Object.defineProperty(arr, key, {
          enumerable: true,
          configurable: true,
          get: function () {
            console.log("get");
            return val;
          },
          set: function (newVal) {
            console.log("set", val); //此处打印 "set 1"
            val = val + newVal;
          },
        });
      }
      //1.
        let arr = [1, 2, 3, 4, 5];
        arr.forEach((value, index) => {
          defineArr(arr, index, value);
        });
        arr[0] = "给第一个元素值为一"; //触发set函数
        arr.push(6); //此处不会触发set函数
        console.log(arr); //[1,2,3,4,5,6]

      //2.
      let arr2 = [{ name: "yz" }, { age: 18 }];
      arr2.forEach((value, index) => {
        defineArr(arr2, index, value);
      });
      arr2.forEach((v, index) => {
        v.name = "6666666666"; //不会触发set
      });
      arr2[0]={name:"我直接赋值会触发set"} //{name:6666666666666666666}
      arr2.push[{name:"99999"}] //不会触发set
      arr2.splice(1,1,{name:"88888"}) //触发set


      //从上面可以看出各种情况对于数组的操作会不会触发set函数

1.在vue中数据初始化时候会对每个属性进行调用Object.definedProperty方法,,当给变量赋值时就会触发变量对应的set方法,从而调用视图更新的函数。
2.Object.definedProperty是可以通过数组下标修改值之后触发对应的set方法的,通过push、pop等方法修改数组的值之后是不会触发set方法。
3.Vue中考虑到性能问题,对于数组类型的数据改变自己定义了一套触发响应的规则,通过数组下标修改数组的值是不会触发视图更新的,但是通过push、pop、shift、unshift等方法修改数组的值是可以触发视图更新的。


//此处由于原生jSd的限制Vue不能检测对象属性的添加和删除
<template>
    <div @click="changeName">
    {{name}}
    </div>
</template>
<script>
export default {
  data () {
    return {
      name: { a: 'yz' }
    }
  },
  methods: {
    changeName () {
      this.name.b = '666' // 我添加一个新的属性Vue检测不到
      console.log(this.name) // 这里可以打印出{a:"yz",b:"666"}
    }
  }
}
</script>

2.默认的Object.definedProperty中,对于操作数组的方法,默认只有利用数组中的splice修改数组值时可以触发set(splice(1, 1, value), 相当于通过数组下标修改属性值)。
但因为Vue做了处理,以下变异方法修改数组的值都可以触发视图的更新。
push,pop, shifit,unshift, sort,splice,reverse, 下图为Vue源码:

var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);

var methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
];

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  var original = arrayProto[method];
  def(arrayMethods, method, function mutator () {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    var result = original.apply(this, args);
    var ob = this.__ob__;
    var inserted;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break
      case 'splice':
        inserted = args.slice(2);
        break
    }
    if (inserted) { ob.observeArray(inserted); }
    // notify change
    ob.dep.notify();
    return result
  });
});
/**
 * Define a property.
 */
function def (obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  });
}

从上面可以看出vue中基于原生Array的原型对象创建了一个新对象,重新定义了数组中的一些方法。
将以上方法定义通过defineProperty定义成了响应式, 对于push、unshift、splice方法给数组新增值时还做了特别的处理。在vue中直接操作以上方法修改数组值,视图都能响应。
Object.defineProperty作为vue2.0响应式的核心将在vue3.0 被Proxy取代。因为前者存在一些局限,前者不能监听到对象的属性的增加和删除,以及不能监听到数组通过原生的方法对数组做的修改等问题,后者提供了对更多种类数据的拦截和对对象更好的支持。
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值