Vue set方法使用及源码解析

使用场景:

动态添加一个响应式属性
实例创建之后添加新的属性到实例上,它不会触发视图更新。

由于 JavaScript 的限制,Vue 不能检测以下变动的数组。

通过数组的下标去修改数组的值,数据已经被修改了,但是不触发updated函数,视图不更新,
vue中检测不到对象属性的添加和删除

解决方案:

1).data 对象就像组件状态的模式(schema)。在它上面声明所有的属性让组件代码更易于理解。

2).添加一个顶级响应属性会强制所有的 watcher 重新计算,因为它之前不存在,没有 watcher 追踪它。这么做性能通常是可以接受的(特别是对比 Angular 的脏 检 查,但是可以在初始化时避免。

2.Vue.set,vm.$set() ,方法调用时,页面会全部更新一遍。

3.数组的splice, concat 方法

<div id="app2">
    <p v-for="item in items" :key="item.id">
        {{item.message}}
    </p>
    <button class="btn" @click="btn2Click()">动态赋值</button><br/>    
    <button class="btn" @click="btn3Click()">为data新增属性</button>
</div>
<script src="../../dist/vue.min.js"></script>
<script>
var vm2=new Vue({
    el:"#app2",
    data:{
        items:[
            {message:"Test one",id:"1"},
            {message:"Test two",id:"2"},
            {message:"Test three",id:"3"}
        ]
    },
    methods:{
        btn2Click:function(){
            Vue.set(this.items,0,{message:"Change Test",id:'10'})
        },
        btn3Click:function(){
            var itemLen=this.items.length;
            Vue.set(this.items,itemLen,{message:"Test add attr",id:itemLen});
        }
    }
});
</script>

set源码解析

定义位置 global-api/index.js
Vue.set()

// 静态方法  set/delete/nextTick
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick

vm.$set()
instance/index.js

// 注册vm 的 $data/ $props/ $set / $delete / $watch
// instance/state.js
stateMixin(Vue)

// instance/state.js
Vue.prototype.$set = set
Vue.prototype.$delete = del

Vue.set 与 vm.$set() 是相同函数

/**
 * 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)}`)
  }
  // 判断 target 是否是对象,key 是否是合法的索引
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    // 通过 splice 对 key位置的元素进行替换
    // splice 在 array.js 进行了响应化处理
    target.splice(key, 1, val)
    return val
  }
  // 如果 key 在对象中以及存在直接赋值
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  // 获取 target 中的 observer 对象
  const ob = (target: any).__ob__
  // 如果target 是vue 实例或者$data 直接返回
  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
  }
  // 如果ob不存在, target 不是响应式对象直接赋值
  if (!ob) {
    target[key] = val
    return val
  }
  // 把key 设置为响应式属性
  defineReactive(ob.value, key, val)
  // 发送通知
  ob.dep.notify()
  return val
}

执行过程

  1. set内部先做辅助判断
  2. 判断target是否是对象,key是否是合格的索引
  3. 求数组的长度,调用修改过后的数组方法
  4. 进入处理数组方法中,修改为响应式数据,调用dep.depend()方法更新
  5. 视图更新
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值