一、应用场景
有时候我们会看到如下代码:
在我们使用vue
进行开发的过程中,可能会遇到这样一种情况:当创建vue
实例后,再次给对象赋值时,发现数据并不会自动更新到视图上去; 当我们去阅读vue
文档的时候,会发现有这么一句话:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。 如下代码,给 student
对象新增 age
属性。
data () {
return {
student: {
name: '',
sex: ''
}
}
}
mounted () { // ——钩子函数,实例挂载之后
this.student.age = 24
}
二、原因分析
受 ES5
的限制,Vue.js
不能检测到对象属性的添加或删除,即Vue
未做到脏数据检查。因为 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)
此时控制台会报错:
只可以使用 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
有什么联系呢?
三、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
原型上。
再来看下set
源码:
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()
时传入的参数对应上。
四、延伸阅读
上文讲解了应用Vue.set
及 this.$set
实现动态添加对象属性,视图层随之动态渲染。其实,还有另外一种方法可以解决以上问题,那就是通过添加 this.$forceUpdate()
在修改数据后执行即可。有关 this.$forceUpdate()
的讲解,详参博文🔎《跨平台应用开发进阶(五) :uni-app 实现列表项左划操作》。