Vue响应式原理(含详细代码)

Vue响应式原理介绍

监听Vue实例中的data选项,我们以下用一个普通的js对象模拟data,用Object.defineProperty进行定义属性值的getter和setter方法。当data选择项中的key进行获取和修改时,Vue内部会调用相应的方法。
Vue响应式原理(含详细代码)_第1张图片
主要考虑以下两点:

  1. data中数据修改时,Vue内部时如何监听到message数据的改变
    Object.defineProperty
  2. 当数据发送改变,Vue是如何知道需要那些模板中用到的key进行页面刷新
    发布者订阅者模式
    注:发布者为dep,订阅者为watcher
    每个data中的属性都会对应一个发布者(dep)对象
响应式原理实现代码:
class Vue{
      constructor(options){
        //1.保存数据
        this.$options = options;
        this.$data = options.data;
        this.$el = options.el;
        //2.将data添加到响应式系统中
        new Observer(this.$data)
        //3.处理el
        new Complier(this.$el, this)
        //4.代理this.$data的数据
        Object.keys(this.$data).forEach(key=>{
        this._proxy(key)
        })
      }
      _proxy(key){
        Object.defineProperty(this, key, {
          configurable: true,
          enumerable: true,
          set(newValue){
            this.$data[key] = newValue
          },
          get(){
            return this.$data[key]
          }
        })
      }
    }

    //将data中的属性加入到相应模式中
    class Observer{
      constructor(data){
        this.data = data

        Object.keys(data).forEach(key=>{
          this.defineReactive(this.data, key, data[key])
        })
      }
      defineReactive(data, key, val){
        //一个属性(key)对应一个发布者对象
        const dep = new Dep()
        Object.defineProperty(data, key, {
          enumerable: true,
          configurable: true,
          //将订阅者加入到subs数组中
          get(){
            if(Dep.target){
              dep.addSub(Dep.target)
            }
            return val
          },
          //数据修改时,通知订阅者
          set(newValue){
            if(newValue === val) return 
            val = newValue
            dep.notify()
          }
        })
      }
    }
	//定义发布者:添加、通知订阅者
    class Dep{
      constructor(){
        this.subs = []
      }
      //
      addSub(sub){
        this.subs.push(sub)
      }
      //订阅者更新时,调用watcher中的update()修改视图中的节点值
      notify(){
        this.subs.forEach(sub=>{
          sub.update()
        })
      }
    }
    
    //订阅者
    class Watcher{
      constructor(node, name, vm){
        this.node = node;
        this.name = name;
        this.vm = vm;
        Dep.target = this;
        this.update()
        Dep.target = null
      }
      update(){
        this.node.nodeValue = this.vm[this.name]
      }
    }
    
    //template编译
    const reg = /\{\{(.+)\}\}/
    class Complier{
      constructor(el, vm){
        this.el = document.querySelector(el)
        this.vm = vm
        this.frag = this._createFragment()
        this.el.appendChild(this.frag)
      }
      _createFragment(){
        const frag = document.createDocumentFragment()

        let child;
        while(child = this.el.firstChild){
          this._compile(child)
          frag.appendChild(child)
        }
        return frag
      }
      _compile(node){
        if(node.nodeType === 1){
          const attrs = node.attributes
          if(attrs.hasOwnProperty('v-model')){
            const name = attrs['v-model'].nodeValue
            node.addEventListener('input',e=>{
              this.vm[name] = e.target.value
            })
          }
        }
        //节点编译时,name为订阅者名字,创建对应的watcher实例
        if(node.nodeType === 3){
          if(reg.test(node.nodeValue)){
            const name = RegExp.$1.trim()
            new Watcher(node, name, this.vm)
          }
        }
      }
    }

如果感觉文章对你有帮助,别忘了点个赞哦!!!!!!!!!

你可能感兴趣的:(vue,vue.js,前端,javascript)