什么是变化侦测
Vue.js会自动通过状态生成DOM,并输出到页面显示出来,这个过程叫渲染。在运行时应用内部的状态会不断发生变化,需要需要不停地重新渲染,这时如何确定状态中发生了什么变化?
变化侦测就是用来解决这个问题的,它分为两种类型:一种是"推"(push),一种是"拉"(pull)。Angular和React中的变化侦测都属于"拉",Vue.js的变化侦测属于"推",当状态发生变化时,Vue.js立刻就知道了,而且在一定程度上知道哪些状态变了。
如何追踪变化
在Javascript中,如何侦测一个对象的变化呢?有两种方法,Object.defineProperty和ES6的Proxy。
kotlin
funtion defineReactive(data,key,val){
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get:function(){
return val
},
set:function(newVal){
if(val===newVal){
return
}
val=newVal
}
}
}
如何收集依赖
在getter中收集依赖,在setter中触发依赖
依赖收集在哪
收集到Dep中
scss
export default class Dep{
construcor(){
this.subs=[]
}
addSub(sub){
this.subs.push(sub)
}
removeSub(sub){
remove(this.subs,sub)
}
depend(){
if(window.target){
this.addSub(window.target)
}
}
notify(){
const subs=this.subs.slice()
for(let i=0,l=subs.length;i<l;i++){
subs[i].update()
}
}
}
function remove(arr,item){
if(arr.length){
const index=arr.indexOf(item)
if(index>-1){
arr.splice(index,1)
}
}
}
依赖是谁,收集谁?
什么是Watcher
Watcher是一个中介的角色,数据发生变化时通知它,然后它再通知其他地方。
kotlin
export default class Watcher{
construcgtor(vm,expOrFn,cb){
this.vm=vm
this.getter=parsePath(expOrFn)
this.cb=cb
this.value=this.get()
}
get(){
window.target=this
let value=this.getter.call(this.vm,this.vm)
window.target=undefined
return value
}
update(){
const oldValue=this.value
this.value=this.get()
this.cb.call(this.vm,this.value,oldValue)
}
}
递归侦测所有key
前面介绍的代码只能侦测数据中的某一个属性,我们希望把数据中的所有属性都侦测到,所以要封装一个Observer类,来将一个数据内的所有属性都转换成getter/setter的形式,然后追踪它们的变化
javascript
export class Observer{
constructor(value){
this.value=value
if(!Array,isArray(value){
this.walk(value)
}
}
walck(obj){
const keys=Object.keys(obj)
for(let i=0;i<keys.length;i++){
defineReactive(obj,keys[i],obj[keys[i]])
}
}
}
funtion defineReactive(data,key,val){
** 新增 递归子属性**
if(typeof val ==='objext'){
new Observer(val)
}
let dep=new Dep()
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get:function(){
**新增 收集依赖**
dep.depend()
return val
},
set:function(newVal){
if(val===newVal){
return
}
val=newVal
**新增 触发依赖**
dep.notify()
}
}
}
Object.defineProperty将对象的key转换成getter/setter的形式来追踪是否被修改,无法追踪新增属性和删除属性,所以Vue.js无法侦测添加删除属性的变化,为了解决这个问题,提供了vm. <math xmlns="http://www.w3.org/1998/Math/MathML"> s e t 和 v m . set和vm. </math>set和vm.delete。