目录
  • 1.vue.js功能:
  • 2.observer.js功能(数据劫持):
  • 3.compiler.js功能:
  • 4.dep.js功能:
  • 5.watcher.js功能:

整体分析vue的基本结构如下图所示:(备注:完整代码github地址https://github.com/1512955040/minivue)

上图中,为我们模拟最小vue的整体结构,首先创建一个vue类型,它负责把data中的成员注入到vue实例中,并且转化成getter/setter,observer的作用是数据劫持,对data中的属性进行数据监听,如果数据发生变化会获取到最新的值,并通知dep。compiler的作用是解析每个元素中的指令和差值表达式并替换成相应的数据。dep的作用是添加观察者,当数据发生变化时通知所有的观察者。watcher内部有一个update方法负责更新视图,下面我们用代码的方式一一进行实现。

1.vue.js功能:

1-1负责接收初始化的参数(选项)

1-2负责把data中的属性注入到vue实例,转化成getter/setter

1-3负责调用observer监听data中所有属性的变化

1-4负责调用compiler解析指令/差值表达式

类图结构如下:

如上图所示:vue类中有三个属性,分别是$options,$el,$data,这三个属性记录构造函数中传过来的参数。_proxydata为vue类中的方法

所以以_开头的成员就是私有成员,这个方法的功能是把data中的属性转化为getter和setter注入到vue实例中。

class vue{
    constructor(options) {
        //1.通过属性保存选项中的数据
        this.$options = options || {}
        this.$data = options.data || {}
        this.$el = typeof options.el === 'string' ? document.queryselector(options.el) : options.el
        //2.把data中的成员转化为getter和setter,注入到vue实例中
        this._proxydata(this.$data)
        //3.调用observer对象,监听数据的变化
        new observer(this.$data)
        //4.调用compiler对象,解析指令和差值表达式
        new compiler(this)
    }
    //把vue的属性转化为getter和setter,注入到vue实例中
    _proxydata(data){
        //遍历data中所有属性
        object.keys(data).foreach(key=>{
          //把data的属性注入到vue实例全局中
          object.defineproperty(this,key,{
              enumerable:true,
              configurable:true,
              get(){
                return data[key]
              },
              set(newvalue){
                if(newvalue===data[key]){
                    return 
                }
                data[key]=newvalue
             }
        })    
      })
    }
}

2.observer.js功能(数据劫持):

2-1 负责把data选项中的属性转化为响应式数据

2-2 data中的某个属性也是对象,把该属性转化为响应式数据

2-3 数据变化发送通知

类图结构如下:

如上图所示:

walk方法的作用是遍历data中的所有属性,definereactive是定义响应式数据,也就是通过调用definereactive方法把属性转化为getter和setter。

class observer{
    constructor(data) {
        this.walk(data)
    }
    //walk方法遍历data中的所有属性
    walk(data) {
        //1.判断data是否对象
        if(!data || typeof data !=='object'){
            return 
        }
        //2.遍历data对象的所有属性
        object.keys(data).foreach(key=>{
            this.definereactive(data,key,data[key])
        })
    }
    //deginereactivce方法定义响应式数据 把属性转化为getter和setter
    definereactive(obj,key,val) {
        let that=this
        // 负责收集依赖,并发送通知
        let dep=new dep()
        //如果val传入对象的话也给对象里面的属性添加getter和setter方法
        this.walk(val)
        object.defineproperty(obj,key,{
            enumerable:true,
            configurable:true,
            get(){
                // 收集依赖
                dep.target && dep.addsub(dep.target)
                return val
            },
            set(newvalue){
                if(newvalue==val){
                    return 
                }
                val=newvalue
                //如果给属性重新赋值成对象,给对象里面的属性重新添加getter和setter方法
                //比如:历史数据vm.msg="hello world" 修改之后vm.msg={a:'hwllo world'}
                //再次调用此方法给vm.msg.a重新添加getter和setter方法
                that.walk(newvalue)
                //发送通知
                dep.notify()
            }
        })
    }
}

3.compiler.js功能:

3-1 负责编译模板,解析指令/差值表达式

3-2 负责页面的首次渲染

3-3 当数据变化后重新渲染视图

类图结构如下:

如上图所示:

el为构造函数传过来的options.el,vm是vue的实例,下面都是vm的方法,对dom进行操作。compile方法内部遍历dom对象的所有节点,并且

判断这些节点是文本节点,如果是文本节点解析差值表达式,如果是元素节点解析指令,istextnode和iselementnode方法判断是文本节点还

是元素节点。compileelement和compiletext方法解析差值表达式和指令。isdirective这个方法判断元素属性是否是指令。

4.dep.js功能:

4-1 收集依赖,添加观察者(watcher)

4-2 通知所有观察者

如上图所示:

在vue的响应式机制中,使用观察者模式来响应数据的变化,dep的作用是收集依赖,在getter方法中收集依赖,在setter方法中通知依赖,每

一个响应式的属性都会场景一个dep对象,负责收集所有依赖于该属性的地方,所有依赖于该属性的位置都会创建一个watcher对象,所以

dep就是收集于该属性的watcher对象,使用setter方法去通知依赖,当属性发生变化时调用nodify方法去发送通知,然后调用watcher对象

的update方法。

类的机构如下图:

如上图所示:

subs是一个数组,存储dep中所有的watcher,addsub方法添加watcher,notify方法发布通知

class dep{
    constructor() {
        //存储所有的观察者
        this.subs=[]
    }
    // 添加观察者
    addsub(sub){
        if(sub && sub.update) {
            this.subs.push(sub)
        }
    }
    //发送通知
    notify(){
        this.subs.foreach(sub =>{
            sub.update()
        })
    }
}

5.watcher.js功能:

5-1 当数据变化触发依赖,dep通知所有的watcher实例更新视图

5-2 自身实例化的时候往dep对象中添加自己

如上图所示:

data中的每一个属性都要创建一个dep对象, 在收集依赖的时候把所有对象的watcher添加到dep对象的subs数组中,在setter对象中发送通

知,调用dep对象的notify方法通知所有关联的watcher对象更新视图。

类图结构如下:

如上图所示:

update对象更新视图,cb回调函数,指明如何更新视图。在更新视图的时候需要一个属性key(data中的属性名称),oldvalue是key 对应的值。

class watcher{
    constructor(vm,key,cb) {
        this.vm=vm
        //data中的属性名称
        this.key=key
        //回调函数负责更新视图
        this.cb=cb
        //把watcher对象记录到dep类的静态属性target
        dep.target =this
        //触发get方法,在get方法中会调用addsub
        this.oldvalue=vm[key]
        dep.target=null
    }
    //当数据发生变化时更新视图
    update(){
        let newvalue=this.vm[this.key]
        if(this.oldvalue === newvalue){
            return
        }
        this.cb(newvalue)
    }
}

下面通过这张图作整体流程的总结:

到此这篇关于vue模拟响应式原理底层代码实现的示例的文章就介绍到这了,更多相关vue 响应式原理内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!