vue 的数据响应式
Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象,而当你修改它们时,视图会进行更新,这使得状态管理非常简单直接。
简单来说就是当对象传入 Vue ,Vue 将遍历此对象并对其进行监听并加以改造,在此基础之上生成新的代理。每次的读写都会被Vue监控,Vue会在其变化时更新UI。
vue 如何追踪变化?
const vm = new Vue({data:{n:0}})
- 当一个普通的 JavaScript 对象传入 Vue 实例作为
data选项,Vue 将遍历此对象所有的 property,并使用Object.defineProperty把这些 property 全部转为getter/setter。 - 也就是说对象一旦传入vue,vue就会对其进行监听并加以改造,在此基础之上生成新的代理。同理vue对methods和computed也有处理。这样在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时及时通知变更。
例:声明一个myData,初始值为 0 。要求当新值如果小于0则返回初始值,大于0则返回新值。利用vue的逻辑处理
let myData = {n:0}
let data2 = proxy2({data:myData});//括号里是匿名对象,无法访问。
function proxy2({data}){
let value = data.n
delete data.n
//用value 存储这个n,然后把原始数据n删掉。(删除不写也行,因为下面生成虚拟对象会覆盖它)
Object.defineProperty(data, 'n', {
get(){
return value
},
set(newValue){
if(newValue<0) return
value = newValue
//如果小于0就返还不给赋值,如果大于0就将新的值赋值给data
}
})
//以上是监听。用value记录原始的n,利用get/set得到新的虚拟属性n,不管myData是否被修改只要经过proxy2都会被监听到。
//下面是代理,用obj代理data。
let obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0) return
data.n = value
}
})
return obj
}
console.log(`结果:${data2.n}`)
myData.n = -1
console.log(`结果:${data2.n}`)
myData.n = 1
console.log(`结果:${data2.n}`)
输出结果为:
代码及效果链接:js.jirengu.com/refujeheji/…
注意事项
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。
对于对象
由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
例一:
new Vue({
data:{},
template:`
<div>{{n}}</div>
`,
}).$mount("#app")
实例没有定义 n ,Vue会给出一个警告,不会在页面进行渲染。因为无法在没有定义的情况下使用n。
例二:
new Vue({
data:{
obj:{
a:0
//obj.a会被vue监听和代理
}
},
template:`
<div>
{{obj.b}}
<button @click="setB">set b</button>
</div>
`,
methods:{
setB(){
this.obj.b = 1;
}
}
}).$mount("#app")
//点击按钮,页面会出现 1 吗?
不会。
- Vue检查到了
obj.a,是有定义的内容,所以没有警告。但这里vue只监听到了obj.a。没有监听到obj.b。对于obj.b进行赋值变更vue是不知道的。
解决方式:
Vue.set(this.object, propertyName, value)this.$set(this.object,propertyName, value)
- 两种方法作用相同,会帮助新增 key,自动创建代理和监听。同时会触发UI更新
例如
methods:{
setB(){
Vue.set(this.obj, 'b', 1)
}
}
或者
methods:{
setB(){
this.$set(this.obj, 'b', 1)
}
}
对于数组
Vue 不能检测以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue - 当你修改数组的长度时,例如:
vm.items.length = newLength
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
解决方式:
Vue.set(this.object, propertyName, value)this.$set(this.object,propertyName, value)- 七种数组变更方法 (这7个API在Vue中被更改,调用后会更新 UI)
| 方法 | 作用 |
|---|---|
| push() | 后面新增 |
| pop() | 弹出最后一个 |
| shift() | 弹出第一个 |
| unshift() | 在第一个新增 |
| splice() | 从中间删除某一项 |
| sort() | 重新排序 |
| reverse() | 倒序 |
这些方式会更新 UI,但不会创建监听和代理。
例如:声明一个数组,如何将["a","b","c"]变成["a","b","c",“d”]
new Vue({
data:{
array:["a","b","c"]
},
template:`
<div>
{{array}}
<button @click="setD">set D</button>
</div>
`,
methods:{
setB(){
this.$set(this.array,3, 'd')
//Vue.set(this.array, 3, 'd')
//this.array.push("d")
}
}
}).$mount("#app")
测试题
方方老师出的几道题:《Vue 自测题》中答错率最高的题的解释 - 知乎 (zhihu.com)