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"
  }
})

猜你喜欢

转载自blog.csdn.net/weixin_45449504/article/details/102824201
今日推荐