模拟Vue响应式

71 阅读1分钟

image.png

vue(实例化静态变量,成员变量)

1、存储vue的options

2、存储vue的el

3、存储vue options的data,存放到$data

4、把$data里面的数据挂载到vue实例上,

Object.keys(this.$data).forEach(key=>{
    Object.defineProperty(this,key,{
        //可枚举 
        //可配置
        //setter
        //getter   
    })
})
//这里不用递归赋值

5、调用observe对象,生成getter,setter函数

6、调用compile对象,生成相应的模板

Observe(对于vue中的对象生成响应式对象)

1、构造函数,接受data

constructor(data){
    this.walk(data))
}

2、walk 循环data中的属性

if(!data || type data!=='object'){
    return
}
object.keys(data).forEach(key=>{
    this.defineReactive(data,key,data[key])
})

3、defineReactive 针对于每个属性生成相应的getter,setter方法

defineReactive(data,key,val){
    //这里传入val避免调用get方法时发生死循环
    let dep=new Dep()
    //递归调用,生成getter,setter
    this.walk(val)
    object.defineProperty(data,key,{
        ...
        set(newVal){
            if(newVal!==val){
           data[key]=newVal
           //如果重新赋值为对象也需要生成getter,setter 响应式 
           this.walk(newVal)   
               dep.notify()//发布通知
           }     
        },
        get(){
            //收集依赖,添加watcher
            Dep.target&&Dep.addSubs(Dep.target)
            return val        
        }    
    })
}

compile(模板数据解析为dom数据)

1、接受vue实例

2、存储vue的el

3、遍历el中的vnode 解析相关的内容

4、判断是否为文本标签/元素节点(isTextNode,isElementNode)

5、元素节点需要判断是否由指令(v-*) (isDrective)

6、为文本标签,解析内容是否为差值表达式

let reg = /{{(.+?)}}/
let value=node.textContent
if(reg.test(value)){
    let key=RegExp.$1.trim()
    node.textContent=value.replace(reg,this.vm[key])
    }

7、元素节点解析相应的指令

Array.from(node.attributes).forEach(attr=>{
    let attrName=attr.name
  if(this.isDrective(attrName)){
      attrName=attrName.substr(2)
      let key=attr.value
      this.update(node,key,attrName)
  }
})
update(node,key,attrName){
    let updateFn=this[attrName+"Update"]
    updateFn&&updateFn.call(this,node,this.vm[key],key)
}
textUpdater(node,value,key){

8、当解析v-model时为当前input控件添加input事件,触发了set方法->触发了Dep的notify 方法

node.addEventListener('input',()=>{
    this.vm[key]=node.value
}))

Dep(依赖收集,发布通知,观察者模式)

1、初始化subs=[],存放相关的依赖

2,addSub方法,添加依赖

3、notify 调用subs里面的watcher里的update方法

watcher(观察者)

1、参数为vue实例,data中属性名称,回调函数

constructor(vm,key,cb)

2、调用Dep

设置Dep.target=this //当前watcher

获取this.vm[key] 设置为旧值

设置Dep.target=null 防止重复添加

调用this.vm[key] 会调用相应属性的get方法

此时Dep.target有值,会调取Observe里面的属性get方法,Dep.target&&Dep.addSub(Dep.target)

添加依赖

当vue data 里面的值发生变化时,会调取Observe里面属性定义的set方法,调取dep.notify()

通知相应的watcher 调用update方法

\