在Vue中声明组件时,如果使用了对象类型的data选项,模板将找不到data中被声明的数据,如果使用了支持Vue模板的语法检查,开发者将得到错误提示"data proerty in component must be a function"。
Vue会递归地将data选择中的数据加入响应式系统,但这些数据应该是声明时即存在的,下面来看一段提示代码。
<div id="app">
<p>{{title}}</p>
<p>{{profile}}</p>
</div>
<script>
const vue = new Vue({
el:"#app",
data() {
return {
title :"A Vue App",
}
},
created() {
Object.assign(this.$data,{
profile:"This is a Vue App"
})
console.log(this.$data);
},
})
</script>
以上代码中浏览器会抛出一个 “profile is not defined” 的一个错误,这是为什么呢。
这里的profile则是created钩子函数中被赋予data选项的,也就是说profile在实例创建完成后被绑定到组件上的,并没有把profile加入数据响应式系统中,所以Vue给出了一个错误提示。此时的profile只是一个普通的JS属性,而非被Vue观察的对象。
所以我在实际开发中,应将尽可能在实例中被观察的数据预先在data中声明,以任何浏览器所支持的原生API都无法将数据动态加入响应式系统,不过Vue针对这一需求,也提供了相应的机制。
开发者可以使用 $ set方法位data选项动态绑定数据,但其也无法挂载响应式数据到 $ data根节点上,不过,既然这些数据要绑定到 $ data上,何不在初始化时声明呢?
target 目标对象
key 键名
value 键值
this.$set(target, key, value)
下面,将分别演示使用原生方法Object.assign 和 Vue.$set 为自建挂载响应式数据实例。
<div id="app">
<p>{{title}}</p>
<p>{{obj.profile}}</p>
<button @click="toggle">toggle</button>
</div>
<script>
const vue = new Vue({
el:"#app",
data() {
return {
title:'A Vue App',
obj:{} //$set 不能用于根节点data上
}
},
created() {
Object.assign(this.obj,{
profile : "This is a Vue App",
})
console.log("created",this.obj);
},
mounted() {
Object.assign(this.obj,{
profile : "This is a Vue Test App",
})
console.log("mounted",this.obj);
},
methods: {
toggle(){
Object.assign(this.obj,{
profile : "This is a Vue App for test",
})
console.log("toggle",this.obj);
}
},
相比于之前的Object.assign的示例,这次的视图表现更显奇怪,Vue不仅没给出错误提示,还响应了在created钩子函数中所做的修改,为什么呢?
- 在组件实例被创建(beforeCreated和created之间)时,obj对象作为data选项的属性存在于实例中拥有合法的地址。
- 在created钩子函数中,profile挂载到obj对象上,obj.profile有undefined变为被赋予的值。由于obj.profile被赋值时,实例已经过了将数据加入响应式系统的阶段,所以obj.profile未被观察。又因为实例尚未和DOM节点绑定(beforeMount),所以此时修改profile依然可以影响之后的试图表现。
- 在mounted钩子函数和click事件中,由于实例已经绑定到DOM节点上且profile不存在响应式系统中,所以此时修改profile试图没有发生变化。
在created中以$set的方式为对象绑定属性。
created() {
this.$set(this.obj,'profile','This is a Vue App')
console.log("created",this.obj);
},
此时的profile被加入Vue响应式系统,当点击按钮时,视图发生变化。