美文网首页Vue技术
Vue.set()和this.$set()介绍

Vue.set()和this.$set()介绍

作者: wu0228 | 来源:发表于2021-12-09 17:49 被阅读0次

在我们使用vue进行开发的过程中,可能会遇到一种情况:当生成vue实例后,当再次给数据赋值时,有时候并不会自动更新到视图上去; 当我们去看vue文档的时候,会发现有这么一句话:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。 如下代码,给 student对象新增 age 属性

data () {
  return {
    student: {
      name: '',
      sex: ''
    }
  }
}
mounted () { // ——钩子函数,实例挂载之后
  this.student.age = 24
}

受 ES5 的限制,Vue.js 不能检测到对象属性的添加或删除。因为 Vue.js 在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue.js 转换它,才能让它是响应的。
正确写法:this.$set(this.data,”key”,value')

mounted () {
  this.$set(this.student,"age", 24)
}

:: Vue 不允许动态添加根级响应式属性。
例如:

const app = new Vue({
  data: {
    a: 1
  }
  // render: h => h(Suduko)
}).$mount('#app1')

Vue.set(app.data, 'b', 2)
image.png

只可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性,例如

var vm=new Vue({
    el:'#test',
    data:{
        //data中已经存在info根属性
        info:{
            name:'小明';
        }
    }
});
//给info添加一个性别属性
Vue.set(vm.info,'sex','男');

Vue.set()和this.$set()实现原理

我们先来看看Vue.set()的源码:
import { set } from '../observer/index'
Vue.set = set
再来看看this.$set()的源码:
import { set } from '../observer/index'
Vue.prototype.$set = set
结果我们发现Vue.set()和this.$set()这两个api的实现原理基本一模一样,都是使用了set函数。
set函数是从 ../observer/index 文件中导出的,区别在于Vue.set()是将set函数绑定在Vue构造函数上,this.$set()是将set函数绑定在Vue原型上。
function set (target: Array<any> | Object, key: any, val: any): any {
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  if (!ob) {
    target[key] = val
    return val
  }
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

我们发现set函数接收三个参数分别为 target、key、val,其中target的值为数组或者对象,这正好和官网给出的调用Vue.set()方法时传入的参数参数对应上。

image.png

补充知识---响应式原理

首先,我们要了解Vue是如何实现数据的双向绑定的!

把一个普通 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,这也就是为什么 Vue 不支持 IE8** 以及更低版本浏览器的原因。
**
知识补充:
  访问器属性不包含数据值,他们包含一对getter函数和setter函数(这两个函数不是必须的)。在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter函数并传入新值,这个函数负责决定如何处理数据。访问器属性不能直接定义,必须是用Object.defineProperty()来定义。

下面是一个例子:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>vue</title>
</head>
<body>
  <script>
    var book={
        _year:2004,
        edition:1
    };
    Object.defineProperty(book,"year",{
        get:function(){
            return this._year;
        },
        set:function(newValue){
            if(newValue>2004){
                this._year=newValue;
                this.edition+=newValue-2004;
            }
        }
    });
    console.log(book.year); // 2004  在读取访问器属性时会调用get函数
    book.year=2005;  // 在给访问器属性赋值时会调用set函数
    console.log(book.edition); // 2
  </script>
</body>
</html>

所以,当对象下的访问器属性值发生了改变之后(vue会将属性都转化为访问器属性,之前提到了), 那么就会调用set函数,这时vue就可以通过这个set函数来追踪变化,调用相关函数来实现view视图的更新。
 每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

image.png
即在渲染的过程中就会调用对象属性的getter函数,然后getter函数通知wather对象将之声明为依赖,依赖之后,如果对象属性发生了变化,那么就会调用settter函数来通知watcher,watcher就会在重新渲染组件,以此来完成更新。

变化检测问题

收到现代JavaScript浏览器的限制,其实主要是 Object.observe() 方法支持的不好,Vue不能检测到对象的添加或者删除。然而Vue在初始化实例时就对属性执行了setter/getter转化过程,所以属性必须开始就在对象上,这样才能让Vue转化它。
  所以对于前面的例子就不能理解了 --- 数组中index都可以看做是属性,当我们添加属性并赋值时,Vue并不能检测到对象中属性的添加或者删除,但是其的确是添加或删除了,故我们可以通过console看到变化,所以就没有办法做到响应式; 而在第二个例子中,我们是在已有的属性的基础上进行修改的,这些属性是在最开始就被Vue初始化实例时执行了setter/getter的转化过程,所以说他们的修改是有效的,model的数据可以实时的在view层中得到相应。

补充知识: 什么是 Object.observe() ?

在介绍之前,不得不残忍的说,尽管这个方法可以在某些浏览器上运行,但事实是这个方法已经废弃
  概述此方法用于异步地监视一个对象的修改。当对象的属性被修改时,方法的回调函数会提供一个有序的修改流,然而这个接口已经从各大浏览器移除,可以使用通用的 proxy 对象      
  方法
<pre style="margin: 0px; padding: 0px; transition-duration: 0.2s; transition-property: background-color, border-color, border-radius, padding, margin, color, opacity; overflow: auto; font-family: "Courier New"; font-size: 12px; overflow-wrap: break-word;">Object.observe(obj, callback[, acceptList])</pre>
  其中obj就是被监控的对象, callback是一个回调函数,其中的参数包括changes和acceptList,
**changes**一个数组,其中包含的每一个对象代表一个修改行为。每个修改行为的对象包含:

  • name: 被修改的属性名称。
  • object: 修改后该对象的值。
  • type: 表示对该对象做了何种类型的修改,可能的值为"add", "update", or "delete"
  • oldValue: 对象修改前的值。该值只在"update"与``"delete"有效。
    acceptList在给定对象上给定回调中要监视的变化类型列表。如果省略, ["add", "update", "delete", "reconfigure", "setPrototype", "preventExtensions"] 将会被使用。
var obj = {
  foo: 0,
  bar: 1
};

Object.observe(obj, function(changes) {
  console.log(changes);
});

obj.baz = 2;
// [{name: 'baz', object: <obj>, type: 'add'}]

obj.foo = 'hello';
// [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}]

delete obj.baz;
// [{name: 'baz', object: <obj>, type: 'delete', oldValue: 2}]

如上所示: 但是chrome也是不支持的,浏览器的兼容性如下:

image

参考文档: Object.ovserve()

推荐阅读文章: Object.observe() 引爆数据绑定革命

相关文章

  • Vue.set()和this.$set()介绍

    在我们使用vue进行开发的过程中,可能会遇到一种情况:当生成vue实例后,当再次给数据赋值时,有时候并不会自动更新...

  • vue强制更新

    使用update this.$forceUpdate(),强制视图更新 用vue.set this.$set(th...

  • Vue.set()和this.$set()的使用

    Vue.set()和this.$set()介绍: 在我们使用vue进行开发的过程中,可能会遇到一种情况:当生成vu...

  • Vue.set()和this.$set()

    在我们使用vue进行开发的过程中,可能会遇到一种情况:当生成vue实例后,当再次给数据赋值时,有时候并不会自动更新...

  • Vue.set() 和this.$set()

    这两个方法其实是差不多的,可以说几乎是一样的,只不过set是绑定在Vue构造函数上,$set()是绑定在Vue原型...

  • Vue.set和Vue数据监测

    Vue.set this.$set(this.student,'添加的属性','添加的属性值') Vue数据监测 ...

  • Vue.set()实现数据动态响应

    this.$set()和Vue.set()本质方法一样,前者可以用在methods中使用。 在vue里面,我们操作...

  • Vue.set()实现数据动态响应!!!

    this.$set()和Vue.set()本质方法一样,前者可以用在methods中使用。 在vue里面,我们操作...

  • 深入理解Vue的数据响应式

    【目录】一、Vue对data做了什么?二、数据响应式三、Vue.set和this.$set 【正文】在学习Vue的...

  • vue.set/this.$set

    vue项目中,几个表格分开展示,但是展示的内容都一样(不展示的内容有区别),为了省事儿,就给封装了个子组件,但是在...

网友评论

    本文标题:Vue.set()和this.$set()介绍

    本文链接:https://www.haomeiwen.com/subject/sswffrtx.html