Vue2双向绑定demo实现

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
  <div id="app">
    测试双向绑定demo
    <input type="text" v-model="textMy" /> {{textMy}}
  </div>
</body>
<script type="text/javascript">
//编译函数
function compile(node, vm) {
  var reg = /\{\{(.*)\}\}/; // 来匹配{{xxx}}中的xxx
  //如果是元素节点
  if(node.nodeType === 1) {
  var attr = node.attributes;
  //解析元素节点的所有属性
  for(let i = 0; i < attr.length; i++) {
    if(attr[i].nodeName == 'v-model') {
      var name = attr[i].nodeValue //看看是与哪一个数据相关
      node.addEventListener('input', function(e) { //将与其相关的数据改为最新值
      vm[name] = e.target.value
    })
      node.value = vm.data[name]; //将data中的值赋予给该node
      node.removeAttribute('v-model')
    }
  }
}

//如果是文本节点
if(node.nodeType === 3) {
  if(reg.test(node.nodeValue)) {
      var name = RegExp.$1; //获取到匹配的字符串
      name = name.trim();
      node.nodeValue = vm[name]; //将data中的值赋予给该node
      new Watcher(vm, node, name) //绑定一个订阅者
    }
  }
}

// 在向碎片化文档中添加节点时,每个节点都处理一下
function nodeToFragment(node, vm) {
  var fragment = document.createDocumentFragment();
  var child;
  while(child = node.firstChild) {
    compile(child, vm);
    fragment.appendChild(child);
  }
  return fragment
}

// Vue构造函数
// 观察data中的所有属性值,注意增添了observe
function Vue(options) {
  this.data = options.data;
  observe(this.data, this)
  var id = options.el;
  var dom = nodeToFragment(document.getElementById(id), this)
  //处理完所有节点后,重新把内容添加回去
  document.getElementById(id).appendChild(dom)
}

//实现一个响应式监听属性的函数。一旦有赋新值就发生变化
function defineReactive(obj, key, val) {
  var dep = new Dep(); //观察者实例
  Object.defineProperty(obj, key, {
  get: function() {
      if(Dep.target) { //每一个观察着都是唯一的
      dep.addSub(Dep.target)
    }
    return val
  },
  set: function(newVal) {
    if(newVal === val) {
      return
  }
    val = newVal;
    console.log('新值' + val);
    //一旦更新立马通知
    dep.notify();
    }
  })
}

//实现一个观察者,对于一个实例 每一个属性值都进行观察。
function observe(obj, vm) {
  for(let key of Object.keys(obj)) {
      defineReactive(vm, key, obj[key]);
    }
   }

// Watcher监听者
function Watcher(vm, node, name) {
  Dep.target = this;
  this.vm = vm;
  this.node = node;
  this.name = name;
  this.update();
  Dep.target = null;
 }

Watcher.prototype = {
  update() {
  this.get();
    this.node.nodeValue = this.value //更改节点内容的关键
  },
  get() {
      this.value = this.vm[this.name] //触发相应的get
    }
  }

//dep构造函数
function Dep() {
  this.subs = [] // 观察主题添加订阅者
}
Dep.prototype = {
  // 添加订阅者
  addSub(sub) {
    this.subs.push(sub)
  },
// 发布通知
notify() {
    this.subs.forEach(function(sub) {
      sub.update();
    })
  }
}

var vm = new Vue({
  el: 'app',
   data: {
      textMy: '礼记'
    }
  })
</script>

</html>

 

posted @ 2024-06-25 17:24  122www  阅读(4)  评论(0编辑  收藏  举报