vue响应式
1.data变化
1.1 尝试在外部改变data的值
import Vue from "vue";
Vue.config.productionTip = false;
const myData = {
n:0
}
console.log(myData)
const vm = new Vue({
data:myData,
template:`<div>{{ n }}</div>`
}).$mount("#app");
setTimeout(()=>{
myData.n += 100
console.log(myData)
console.log(vm)
},2000)
2秒后myData.n也变为了100,页面上也渲染的是100,但是第7行打印的myData纯粹是一个只含有n的对象,
在15行的时候,myData含有了一堆原本没有的属性
image.png
这是因为使用Object.defineProperty进行了代理,让vm知道data改变,从而进行重新render。
1.2 defineProperty控制getter和setter
let myData1 = {n:0}
let data1 = proxy2({ data:myData1 }) // 括号里是匿名对象,无法访问
function proxy2({data}){
let value = 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(value){
if(value<0)return
data.n = value
}
})
return obj // obj 就是代理
}
console.log(`${data1.n}`)
myData1.n = -1
console.log(`${data1.n},设置为 -1 失败了`)
myData1.n = 1
console.log(`${data1.n},设置为 1 成功了`)
首先使用defineProperty代理myData1,控制它的'n'的 getter和setter,然后创建一个新的对象obj,也用defineProperty控制它的'n',然后返回obj。此时即使用户直接修改原始的myData1的值也无法成功。
1.3 vm = new Vue({data:myData})
1. 会让vm成为myData的代理
2. 会对myData的所有属性进行监控,知道myData变化后进行render操作
Object.defineProperty存在问题
1.1 必须存在obj.n才能监听obj.n
假如之前不存在n,但是在页面上使用n,Vue会给出警告。
const vm = new Vue({
data:{
obj:{
a:1
}
},
template:`<div>{{ obj.b }}</div>`
}).$mount("#app");
但是Vue只会监听第一层,如上图,假如用户只声明了obj.a,但是在页面上使用obj.b,此时Vue不会给出警告,
页面上也无法展示obj.b.** 如果是新增的属性要使用Vue.set才能生效**
**
解决方法:
1.提前声明好需要的key,后面不再添加属性
2.使用Vue.set或者this.$set
Vue.set和this.$set作用
1.新增key
2.创建代理和监听
3.触发UI更新
1.2 数组的变异
数组无法一开始就声明所有需要的下表,假如全部使用Vue.set会非常麻烦。
Vue会拦截数组,将push(),pop(),shift(),unshift(),splice(),sort()以及reverse()修改,自动执行Vue.set操作,然后更新render。
大致实现:
es6实现
class VueArray extends Array{
push(...args){
const oldLength = this.length // this
super.push(...args)
console.log(' 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)
网友评论