Vue2源码解读 - $set()使用及实现原理
- 当我们给响应式的对象新增属性时,新增的属性并不会渲染到页面中
- 对于响应式的数组,增加元素、修改数组长度时,数组的这些变化也不会反映到页面中
那么如何让新增的对象或数组实现响应式及时渲染页面呢?
使用this.$set()
官方定义
Vue 不允许在已经创建的实例上动态添加新的根级响应式属性 (root-level reactive property)。然而它可以使用 Vue.set(object, key, value)
方法将响应属性添加到嵌套的对象上
// Vue.set(object, key, value)
<template>
<div>{{obj.k}}</div>
</template>
<script>
export default {
data() {
return {
obj: {
s: '1',
z: '2'
}
}
},
mounted() {
this.$set(this.obj, 'k', '3')
}
}
</script>
$set原理
直接看源码
function set(target: Array<any> | Object, key: any, val: any): any {
// isUndef 是判断 target 是不是等于 undefined 或者 null 。
//isPrimitive 是判断 target 的数据类型是不是 string、number、symbol、boolean 中的一种
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
}
// vue给响应式对象(比如 data 里定义的对象)都加了一个 __ob__ 属性,
// 如果一个对象有这个 __ob__ 属性,那么就说明这个对象是响应式对象,我们修改对象已有属性的时候就会触发页面渲染。
// 非 data 里定义的就不是响应式对象。
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
}