欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

vue 数据双向绑定 个人实现版

程序员文章站 2022-04-11 21:24:21
html
{{text}} ......

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]
    }
  }
}