天天看点

Vue的双向绑定原理详解Vue的双向绑定原理

Vue的双向绑定原理

Vue采用数据劫持结合发布者-订阅者模式的方法,通过Object.defineProperty()来劫持各个属性的setter,getter属性,在数据变动话,通知订阅者,触发更新回调函数,重新渲染视图

关键元素
observer 实现对vue各个属性进行监听
function observer(obj, vm){
      Object.keys(obj).forEach(function(key){
          defineReactive(vm, key, obj[key])
  })
}
// Object.defineProperty改写各个属性
function defineReactive( obj, key, val ) {
    // 每个属性建立个依赖收集对象,get中收集依赖,set中触发依赖,调用更新函数 
    var dep = new Dep();
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function() {
          // 收集依赖  Dep.target标志
          Dep.target && dep.addSub(Dep.target)
          return val
        },
        set: function(newVal){
            if(newVal === val) return
            // 触发依赖
            dep.notify()
            val = newVal
      }
  })
}
           
-Dep实现
function Dep(){
    this.subs = []
}
Dep.prototype = {
    constructor: Dep,
    addSub: function(sub){
        this.subs.push(sub)
    },
    notify: function(){
        this.subs.forEach(function(sub){
            sub.update() // 调用的Watcher的update方法
      })
    }
}
           
compiler 实现对vue各个指令模板的解析器,生成AST抽象语法树,编译成Virtual Dom,渲染视图
function compiler(node, vm){
    var reg = /\{\{(.*)\}\}/;
    // 节点类型为元素
    if(node.nodeType ===1){
        var attr = node.attributes;
        // 解析属性
        for(var i=0; i< attr.length;i++){
                if(attr[i].nodeName == 'v-model'){
                var  _value = attr[i].nodeValue
                 node.addEventListener('input', function(e){
                    //给相应的data属性赋值,触发修改属性的setter
                    vm[_value] = e.target.value
                })
                node.value = vm[_value] // 将data的值赋值给node
                node.removeAttribute('v-model')
            }
        }
        new Watcher(vm,node,_value,'input')
    }
    // 节点类型为text
    if(node.nodeType ===3){
       if(reg.test(node.nodeValue)){
           var name = RegExp.$1; 
            name = name.trim()
            new Watcher(vm,node,name,'input')
       }
    }
}
           
Watcher 连接observer和compiler,接受每个属性变动的通知,绑定更新函数,更新视图
function Watcher(vm,node,name, nodeType){
    Dep.target = this; // this为watcher实例
    this.name = name
    this.node = node 
    this.vm = vm
    this.nodeType = nodeType
    this.update() // 绑定更新函数
    Dep.target = null //绑定完后注销 标志
}
Watcher.prototype = {
    get: function(){
        this.value = this.vm[this.name] //触发observer中的getter监听
  },
   update: function(){
      this.get()
      if(this.nodeType == 'text'){
        this.node.nodeValue = this.value
      }   
      if(this.nodeType == 'input') {
          this.node.value = this.value
    }
  }
}
           

完整实现

function Vue(options){
    this.date = options.data
    var data = this.data
    observer(data, this) // 监测
    var id = options.el
    var dom = nodeToFragment(document.getElmentById(id),this) //生成Virtual Dom
  // 编译完成后,生成视图
    document.getElementById(id).appendChild(dom)    
 }
function nodeToFragment(node, vm){
    var flag = document.createDocumentFragment()
    var child
    while(child = node.firstChild){
        compiler(cild, vm)
        flag.appendChild(child)
  }
  return flag
}

// 调用
  var vm = new Vue({
    el: "app",
    data: {
        msg: "hello word"
  }
})