Skip to content

Vue双向绑定原理解析。😃

javascript
<script>

//双向绑定的原理
let doc = document

let log = console.info.bind(console)

function node2Fragment(node, vm) {
  let temp = doc.createDocumentFragment()
  let child
  while (child = node.firstChild) {
  	complie(child, vm)
  	temp.appendChild(child)
  }
  return temp
}

function complie(node, vm) {
  let  reg = /\{\{(.*)\}\}/
  let type = node.nodeType
  switch(type) {
    case  1:
        let attr = node.attributes;
        for ( let i = 0; i < attr.length; i++ ) {
            if (attr[i].nodeName == 'v-model') {
              let name = attr[i].nodeValue
	          node.addEventListener('input', function(e) {
		      vm[name] = e.target.value
	        })
	          node.value = vm[name]
	          node.removeAttribute('v-model')
            }
        }
        break
    case  3:
        if (reg.test(node.nodeValue)) {
            let name = RegExp.$1
            name = name.trim()
            node.nodeValue = vm[name]
            new Watcher(vm, node, name)
        }
        break
    }
}

function observe(obj, vm) {
  Object.keys(obj).forEach(function(key) {
    defineReactive(vm, key, obj[key])
  })
}

function defineReactive(obj, key, val) {
  let dep = new Dep()
  Object.defineProperty(obj, key, {
    get: function() {
       if (Dep.target) dep.addSub(Dep.target)
       return val
    },
    set: function(newVal) {
       if (val === newVal) return
       val = newVal
       dep.notify()
    }
  })
}

function Watcher(vm, node, name) {
  Dep.target = this
  this.name = name
  this.node = node
  this.vm = vm
  this.update()
  Dep.target = null
}

Watcher.prototype = {
  update: function() {
    this.get()
    this.node.nodeValue = this.value
  },
  get: function() {
    this.value = this.vm[this.name]
  }
}

function Dep() {
  this.subs = []
}

Dep.prototype = {
  addSub: function(sub) {
    this.subs.push(sub)
  },
  notify: function() {
    this.subs.forEach(function(sub) {
      sub.update()
    })
  }
}

function Vue(options) {
  this.data = options.data
  let data = this.data
  observe(data, this)
  let id = options.el
  let dom = node2Fragment(doc.getElementById(id), this)
  doc.getElementById(id).appendChild(dom)
}

</script>
<body>
<div id="app">
   <input type="text" v-model="text" />
   {{text}}
</div>
</body>
<script>
  var vm=new Vue({
    el:'app',
       data:{
          text:'hello world!'
       }
    })
</script>

Powered by VitePress.