数据响应式
什么是响应式?
- 若一个物体能对外界的刺激做出反应,那它就是响应式
- 例:我打你,你会喊疼,这就是响应式
Vue的data 是响应式
const vm = new Vue({data:{n:0})- 如果我修改了 vm.n,那么 UI中的 n就会响应我
例子
const myData = {
n: 0
}
console.log(myData)
const vm = new Vue({
data: myData,
template: `
<div>{{n}}<button @click = 'add'>+10</button</div>
`,
methods: {
add() {
myData.n += 10
}
}
}).$mount('#app')
setTimeout(() => {
myData.n += 10
console.log(myData)
}, 3000)
/*
第一次打印
{n: 0}
第二次个打印及内部
{__ob__: we}
n: (...)
__ob__: we {value: {…}, dep: ce, vmCount: 1}
get n: ƒ ()
set n: ƒ (t)
__proto__: Object
*/
解析
-
data的值n发生了变化,这就是响应式
-
第二次内部的
{n:(...)}说明 n 并不是真实的存在,而是有一个get n 和 set n,他们来模拟对n 的读写操作 -
而且Vue让vm 成为 myData的代理,会对myData的属性进行监控,为了防止myData的属性变了,但vm却不知道
-
为什么要让vm 知道?知道属性变了才可以调用render(data)
Object.defineProperty和data的联系
- 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter
- getter/setter 用于对属性的读写进行监控
例子:需求n不能小于0
let myData = { n: 0 }
let data5 = proxy({ data: myData })
function proxy({ data }) {
//首先拿到这个值
let value = data.n
//然后这里在从新设置n,原来的data.n 就被替代了
Object.defineProperty(data, 'n', {
get() {
return value
},
set(newValue) {
if (newValue < 0) return
value = newValue
}
})
//上面代码话会监听 data
const obj = {}
Object.defineProperty(obj, 'n', {
get() {
return data.n
},
set(val) {
if (val < 0) return
data.n = val
}
})
return obj//代理
}
console.log(`需求五 :${data5.n}`)//0
myData.n = -1
console.log(`需求五 :${data5.n}`)//0
myData.n = 1
console.log(`需求五 :${data5.n}`)//1
data5.n = 2
console.log(`需求五 :${data5.n}`)//2
上面代码中可以看出:
- 当myData.n 发生变化时,会被监听
- 当data5.n 发生变化时也会被监听
- 这样就可以防止在两个地方都写成小于0了
!!Vue 不能检测数组和对象的变化
对于对象
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')
存在问题:当我点击set b视图不会显示1,Vue没法监听不存在的obj.b
解决办法:使用Vue.set 或者 this.$set
new Vue({
data: {
obj: {
a: 0,//obj.a 会被 Vue监听 和 代理
}
},
template: `
<div>
{{obj.b}}
<button @click='setB'>set b</button>
<button @click ='add'>+1</button>
</div>
`,
methods: {
setB() {
Vue.set(this.obj,'b',1)//它会传给Object.defineProperty(obj,'b',{b:1})
//this.$set(this.obj,'b',1)第二种写法,和前面一个写法是相等的,没有区别
},
add() {
this.obj.b += 1//因为前面已经设置了,所以这里的 obj.b 是已经存在了
}
}
}).$mount('#app')
对于数组
new Vue({
data: {
array: ['a', 'b', 'c']
//数组你可以理解为 array:{0:'a',1:'b',2:'c'}
},
template: `
<div>
{{array}}
<button @click='setD'>set d</button>
</div>
`,
methods: {
setD() {
//this.array[3] = 'd'//这样根本不会设置,它里面没有 array[3],它观察不到
Vue.set(this.array, 3, 'd')
}
}
}).$mount('#app')
存在问题: this.array[3] = 'd' 这样不会显示,Vue.set(this.array, 3, 'd') 这样可以,但是,这可能就是用户的数据,根本不知道有多少
解决办法:Vue中有7个API都被篡改了,pop、push、reverse、shift、sort、splice、unshift,调用后会更新UI,其中 push方法就可以使用,它可以直接添加到上面
new Vue({
data: {
array: ['a', 'b', 'c']
//数组你可以理解为 array:{0:'a',1:'b',2:'c'}
},
template: `
<div>
{{array}}
<button @click='setD'>set d</button>
</div>
`,
methods: {
setD() {
this.array.push('d')
}
}
}).$mount('#app'