从下面这个小例子带你走进Vue数据响应式
const myData = {
n: 0
}
console.log(myData)
new Vue({
data: myData,
template: `
<div>{{n}}</div>
`
}).$mount("#app");
setTimeout(()=>{
myData.n += 10
console.log(myData)
},3000)
Object {n: 0}
Object {n: (...)}
一个对象作为数据传递给Vue后,变得不一样了,究竟发生了什么?
const vm = new Vue({data:myData})做了什么?
1.vm
成为myData
的代理(proxy),如果没有vm
,this
会代指这个对象
2.对myData
的所有属性进行监听,一旦myData
的属性发生变化,vm
立即知道
3.属性变化后,调用render(data),UI = render(data)
const data = {a:1,b:2}
function proxy({data}) {
const obj = {}
for (let key in data) {
//监听data
let value = data[key]
Object.defineProperty(data, key, {
get() {
return value
},
set(v) {
value = v
}
})
// obj 就是代理
Object.defineProperty(obj, key, {
get() {
return data[key]
},
set(value) {
data.n = value
}
})
}
return obj
}
console.log(data)
let vm = proxy({data:data})
console.log(data)
//在外部修改数据,看vm是否也跟着发生变化
data.a = 2
console.log(vm.a)
Object {a: 1,b: 2}
Object {a: (...),b: (...)}
2
同理,Vue对methods和computed也有处理
数据响应式
当数据发生改变后,
Vue
会通知到使用该数据的代码,自动视图更新
Vue
只监听第一层属性的变化,不监听嵌套属性,否则效率低
因为
obj.b
没有在data
中没有,所以不会被监听,undefined
状态的数据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; //请问,页面中会显示 1 吗?
}
}
}).$mount("#app");
解决办法
1.用.set
方法
Vue.set(this.obj,"b",1)
this.$set(this.obj,"b",1)
console.log(Vue.set===this.$set)//true,这两种方式一模一样
Vue.set
做了两件事:新增属性,并自动地对这个属性创建代理和监听
Vue
发现这个属性从undefined
的状态变成1
,触发render
,更新页面
2.提前在data
中声明好,初始值设为undefined
但是,当为数组时,无法提前声明,每次用
Vue.set
很麻烦
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
this.array[3] = "d"; //请问,页面中会显示 'd' 吗?
// 等下,你为什么不用 this.array.push('d')
}
}
}).$mount("#app");
解决方法
this.array.push(value)
console.log(this.array)
通过控制台可以看出,
push
方法变了,Vue
篡改了数组的API
变异方法文档
Vue的变异方法实现思路(并不是源码)
Vue会给这个数组加一层原型,对于数组的变化,自动
Vue.set
篡改push
方法,对于Array
的变化通过Vue.set
通知给Vue
class VueArray extends Array{
push(...args){
const oldLength = this.length //this 当前数组
super.push(...args)//调用父类的push
for(let i = oldLength;i < this.length;i++){
Vue.set(this,i,this[i])
}
}
}
const vueArrayPrototype = {
push: function(){
return Array.prototype.push.apply(this, arguments)
}
}
vueArrayPrototype.__proto__ = Array.prototype //不是标准属性
const array = Object.create(vueArrayPrototype) array.push(1)
总结
-
const vm = new Vue({data:myData})
这个语句中,this
就是vm
,vm
是myData
的代理,所以this.n
能够读取 - Vue没有办法监听和代理新的Key
- 要使用set来新增Key,创建代理和监听,更新UI
- 最好提前把属性都写出来,不要新增Key
- 但是数组做不到「不新增Key」
- 数组也可以用set来新增Key,创建代理和监听,更新UI
- Vue为解决这个困扰,篡改7个数组API
- 这7个API会自动处理监听和代理,并更新UI
网友评论