Vue2 $set 与 set

背景介绍

在使用Vue2进行开发过程我们有时会碰到以下两种情况:

  1. 修改了模型里数组的数据,视图层没进行更新
  2. 向模型层的数据添加了新的属性,视图层没进行更新破题

解决问题

下面我们在 vue 2.6.11 处理进行测试

数组的处理

数组修改元素

这个场景是修改数组的元素,有以下一个组件:

<template>
  <div>
    <ul>
      <li v-for="(item,i) in list" :key="item.value" class="item">
        {{item.name}}
        <button @click="handleClick(i)">修改当前名称</button>
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    name: "Array",
    data(){
      return {
        list: [
          {name: "John",value: 1},
          {name: "Lili",value: 2},
          {name: "White",value: 3},
        ]
      }
    },
    methods:{
      handleClick(i){
        this.list[i] = {
          name: this.list[i].name + Math.floor(100 * Math.random()),
          value: Math.floor(100 * Math.random())
        };
      }
    }
  }
</script>

<style lang="css" scoped>
.item {
  text-align: left;
  margin: 8px auto;
}
</style>

结果如下:

 解决方案:

      handleClick(i){
        const currentItem = {
          name: this.list[i].name + Math.floor(100 * Math.random()),
          value: Math.floor(100 * Math.random())
        };
        // 方法一
        Vue.set(this.list,i,currentItem);
        // 方法二
        this.$set(this.list,i,currentItem);
        // 方法三
        this.list.splice(i,1,currentItem);
      }

以上共有三种解决方法。

数组新增元素

在某个指定的序号添加元素

方法一

<template>
  <div>
    <ul>
      <li v-for="item in list" :key="item.value" class="item">
        {{item.name}}
      </li>
    </ul>
    <button @click="handleAdd">在第一项后新增一个元素</button>
  </div>
</template>

<script>
  export default {
    name: "Array",
    data(){
      return {
        list: [
          {name: "John",value: 1},
          {name: "Lili",value: 2},
          {name: "White",value: 3},
        ]
      }
    },
    methods:{
      handleAdd(){
        const addItem = {
          name: "Burden",
          value: 9
        };
        const addIndex = 1;
        this.list.length = this.list.length + 1;
        for(let j = this.list.length - 1;j>=addIndex;j--){
          this.$set(this.list,j,this.list[j-1]);
        }
        this.list[addIndex] = addItem;
      }
    },
  }
</script>

<style lang="css" scoped>
.item {
  text-align: left;
  margin: 8px auto;
}
</style>

方法二

方法一 里面 methods 里面的 handleAdd 修改为下面的即可:

handleAdd(){
  const addItem = {
    name: "Burden",
    value: 9
  };
  const addIndex = 1;
  this.list.splice(addIndex,0,addItem);
}

对象新增新的属性

<template>
  <div class="hello">
    <div v-if="obj.flag">
      yes
    </div>
    <div v-else>
      {{obj.data}}
    </div>
    <button @click="handleClick">切换flag</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data(){
    return {
      obj: {
        flag: true
      }
    }
  },
  methods:{
    handleClick(){
      this.$set(this.obj,'data','flag 为false的数据');
      this.obj.flag = !this.obj.flag;
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

源码剖析

自己download vue 2.6.11 的源码,

Vue.set 方法文件 src\core\global-api\index.js ,44-46行见下面

其中 set 引用自文件  src\core\observer\index.js 。

this.$set 方法在文件 src\core\instance\state.js,见下图

 其中 set 同样引用自文件  src\core\observer\index.js 。

可以看出 Vue.set 与 this.$set 引用了同样的函数,下面来看看这个引用的函数。

这个函数在 src\core\observer\index.js 文件中。文件196-213行如下

/**
 * Set a property on an object. Adds the new property and
 * triggers change notification if the property doesn't
 * already exist.
 */
export 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
}

总结:

  1. 数组和对象的修改使用 Vue.set 和 this.$set 的效果是一样的
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值