想要说明白this.persons[0] = {id:001,name:'马老师',age:50,sex:'男'}
问题,你就必须要知道Vue是如何监测数组里面元素的改变
先看Vue是如何让监测对象里的数据改变的,随后引出它是怎么监测数组的
Vue是怎么监测对象里数据的改变的
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data(){
return{
name:'bilibili',
address:'上海'
}
}
})
vm身上有什么:
> vm.name
< "bilibili"
> vm.address
< "上海"
这块叫数据代理,也就是data
这一块数据最终最终vm
一定会有
data
里面的数据放到vm上,执行的vm._data = data
其实是第二步了
第一步是加工data
> vm._data
< v{__ob__: Observer}
address: (...)
name: (...)
__ob__: Observer {value: {…}, dep: Dep, vmCount: 1}
get address: ƒ reactiveGetter()
set address: ƒ reactiveSetter(newVal)
get name: ƒ reactiveGetter()
set name: ƒ reactiveSetter(newVal)
[[Prototype]]: Object
从data
到_data
在_data
的name不再给的直接了,通过get name
去给;当我要去修改name
的时候通过set name
去给
其实所谓的加工就是: 把你data
里面写的每一组k-v
都形成的getter setter写法
你所传入的data
它拿到了之后,它执行了第一件事:加工data
--> 大概是vm._data
加工一下就能做响应式
了,什么是响应式?
数据变了,页面也跟着变
,这就是响应式
当有一天你去修改了_data.name
的时候就会引起set name
的调用,这个set
里面写了一个调用,这个调用就可以重新解析模板
name一改 --> set一调 --> set一调重新解析模板 --> 模板一重新解析,生成新的虚拟DOM --> 新旧DOM对比 --> 更新页面
Vue监测name
和address
的改变就是靠get
和set
若你自己写一个与之类似
let data = {
name: 'bilibili'
}
Object.defineProperty(data,'name',{
get(){
return data.name
}
set(val){
data.name = val
}
})
这样表面没问题,但是不能读取和修改原因是:
如果有人读取name,调get(),get()里面写了data.name,data.name一执行,调get(),get()在执行data.name又执行;递归停不下来了
同理set(val)也是:当有人修改了data中的name,set()就被调用,set()一被调用data.name = val执行,data上的name只要一改就立马调用set()
所以要实现不是这么写的, vue底层写了个构造函数
//Observer:观察者/观察家/观测者/观察者模式
//它能创建一个能监视的实例对象,它能收到一个对象作为参数
function Observer(obj){
}
它想把你data里的数据进行监测
它得靠Observer
它拿到你所传入的data
,它马上做了一件new Observer(data)
事,然后它会收到Observer的实例对象const obs = new Observer(data)
let data = {
name: 'bilibili'
}
//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
我们在Observer(obj)
写一写核心的东西,借助哪个api
?
const keys = Object.keys(obj)
然后开始遍历
在遍历之前看一下keys:console.log(keys)
Array(2)
0: "name"
1: "address"
length: 2
然后开始遍历,你拿到的每一个可以用k
作为形参
keys.forEach((k)=>{
//函数体
})
技巧来了,Object.defineProperty(this)
它往this
里面添,你就要知道this
是谁(this是实例对象不是data
)往Observer
实例对象身上添加一个属性,属性是得读取k
的值(Object.defineProperty(this,k,{})
);get()
中把传入的obj身上所对应的属性的值
交出去(return obj[k]
);set()
会收到你所修改的值,然后把传入的这个对象身上的k所对应的属性
改掉(obj[k] = val
)。这样就够用了
let data = {
name: 'bilibili',
address: '上海'
}
//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
function Observer(obj){
//汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj)
//遍历
console.log(keys)
keys.forEach((k)=>{
//函数体
Object.defineProperty(this,k,{//this是实例对象不是data;实例对象身上添加一个属性
get(){
return obj[k]//把传入的obj身上所对应的属性的值交出去
},
set(val){
obj[k] = val
}
})
})
}
最后obs
长什么样:
console.log(obs)
如果此时此刻准备一个vm
实例对象
//准备一个vm实例对象
let vm = {}
vm._data = obs
data没变,你可以做的更完善一点:
vm._data = data = obs
在set()
里观察,写一个模板字符串
set(val){
console.log(`${k}被改了,我要去解析模板,生成虚拟DOM,进行比较....我要开始忙了`)
obj[k] = val
}
> vm._data === data
< true
> vm._data.name = "陈天羽"
name被改了,我要去解析模板,生成虚拟DOM,进行比较....我要开始忙了
< '陈天羽'
这只是模拟,vue比我们这个做的更好,它可以
> vm._data.name = 123
< 123
> v.name = 456
< 456
我们这里没有考虑对象里面还有对象的情况
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data(){
return{
name:'bilibili',
address:'上海',
students:{
1:{
name: 'tom',
age: 20
}
}
}
},
})
> vm._data
< {__ob__: Observer}
address: (...)
name: (...)
students: Object
1: Object
__ob__: Observer
dep: Dep {id: 6, subs: Array(0)}
value: {__ob__: Observer}
vmCount: 0
[[Prototype]]: Object
get 1: ƒ reactiveGetter()
set 1: ƒ reactiveSetter(newVal)
[[Prototype]]: Object
__ob__: Observer {value: {…}, dep: Dep, vmCount: 1}
get address: ƒ reactiveGetter()
set address: ƒ reactiveSetter(newVal)
get name: ƒ reactiveGetter()
set name: ƒ reactiveSetter(newVal)
get students: ƒ reactiveGetter()
set students: ƒ reactiveSetter(newVal)
[[Prototype]]: Object
无限的找下去,直到没有对象
哪怕你把东西放到数组里面,Vue
也能找到