vue 数据双向绑定 实现逻辑

html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title></title>
  </head>

  <body>
    <div id="app"><input type="text" v-model="text" /> {{text}}</div>
  </body>
  <script src="./Mvvm.js"></script>
  <script type="text/javascript">
    var vm = new Vue({
      el: 'app',
      data: {
        text: '超哥哥',
      },
    })
    vm.data.text = '超哥哥To'
  </script>
</html>

js

class Vue {
  constructor(options) {
    this.data = options.data //创建data 数据
    this.optionsTo = options
    this.observe(this.data, this) //对数据数据进行双向绑定
    let id = options.el // 获取挂载点
    let dom = this.nodeToFrament(document.getElementById(id), this) //创建文档碎片,并处理
    document.getElementById(id).appendChild(dom) //将处理好的文档碎片添加到 文档中
  }
  nodeToFrament(node, vm) {
    let fragment = document.createDocumentFragment() //创建文档碎片
    let child
    while ((child = node.firstChild)) {
      // 这里是一个判断条件条件,当node.firstChild 为null 的时候会停下来
      this.compile(child, vm) //执行对 节点的处理
      fragment.appendChild(child) // 将 处理完的数据添加到 文档碎片容器中
    }
    return fragment
  }
  compile(node, vm) {
    let reg = /{{(.*)}}/ //创建去除双花括号的 正则
    if (node.nodeType === 1) {
      //如果 节点类型等于 1, 那么代表是 元素节点
      let attr = node.attributes //拿到 元素节点的所有属性
      for (let i = 0; i < attr.length; i++) {
        if (attr[i].nodeName == 'v-model') {
          //如果 元素节点中出现 v-model 属性
          let name = attr[i].nodeValue //拿到 属性对应的 值
          node.addEventListener('input', function (e) {
            vm.data[name] = e.target.value
          })
          new Watcher(vm, node, name)
          //   node.value = vm.data[name] //去data 里面查找对应的 数据并赋值给对应 元素
          node.removeAttribute('v-model')
        }
      }
    }
    if (node.nodeType === 3) {
      //如果是 文本节点
      if (reg.test(node.nodeValue)) {
        let name = RegExp.$1 //调用正则
        name = name.trim() //去掉前后空格
        // node.nodeValue = vm.data[name] //去data 里面查找对应的 数据并赋值给对应 元素
        new Watcher(vm, node, name) //实例化Watcher 监听数据
      }
    }
  }
  observe(obj) {
    if (!obj || typeof obj !== 'object') {
      return
    } else {
      Object.keys(obj).forEach((key) => {
        this.defneReactive(obj, key, obj[key])
      })
    }
  }
  defneReactive(obj, key, val) {
    let dep = new Dep() //Dep 函数相当于是一个中间件,桥梁
    this.observe(val)
    Object.defineProperty(obj, key, {
      get() {
        if (Dep.target) {
          dep.addSub(Dep.target) //只要每次获取数据 都将全局变量里面的数据存储到dep 里面,以便设置数据时调用
        }
        return val
      },
      set(newVal) {
        if (newVal === val) {
          return
        } else {
          val = newVal
          dep.notify() //触发notify 方法,dep 里面的数据呈现到页面
        }
      },
    })
  }
}
class Dep {
  //Dep 函数相当于是一个中间件,桥梁
  constructor() {
    this.subs = []
  }
  addSub(sub) {
    this.subs.push(sub)
  }
  notify() {
    this.subs.forEach((sub) => {
      sub.update()
    })
  }
}
class Watcher {
  constructor(vm, node, name) {
    Dep.target = this //Dep.target 是一个全局变量,存储的是 this指向的 Vue 函数里面的数据
    this.vm = vm
    this.node = node
    this.name = name
    this.update()
    Dep.target = null //将 Dep.target 变量清空,从而保证Dep.target 里面的数据每次的是最新的
  }
  update() {
    if (this.node.nodeType === 3) {
      this.node.nodeValue = this.vm.data[this.name] //去data 里面查找对应的 数据并赋值给对应 元素
    } else if (this.node.nodeType === 1) {
      this.node.value = this.vm.data[this.name]
    }
  }
}

  

原文地址:https://www.cnblogs.com/SuperBrother/p/12779161.html