双向绑定 / proxy 代理 整理

class Vue {
  constructor(el, data) {
    this.el = document.querySelector(el)
    this.data = data
    this.domPool = {}
    this.init()
  }
  init() {
    // 数据劫持
    this.Obersrve()
    // 编译
    this.Compile(this.el)
  }

  // 数据劫持 代理
  Obersrve() {
    const _this = this
    // proxy代理
    this.data = new Proxy(this.data, {
      get(target, key) {
        console.log(target, key, 'get');
        return Reflect.get(target, key)

      },
      set(target, key, value) {
        // 如果data改变就修改视图
        _this.domPool[key].innerHTML = value

        return Reflect.set(target, key, value)
      }
    })
  }
  // 编译 {{}} 以及input双向绑定
  Compile(el) {
    const childNodes = el.childNodes
    // 遍历每一个节点 
    childNodes.forEach(node => {
      // 如果是文本节点
      if (node.nodeType === 3) {
        const text = node.nodeValue
        let reg = /\{\{(.*)\}\}/
        if (text.trim().length) {
          // 非空 且 符合格式
          let _isValid = reg.test(text)
          if (_isValid) {
            const _key = text.match(reg)[1].trim()
            this.domPool[_key] = node.parentNode
            //通过reduce 方法 当取到类似{{ info.a }} 中间有. 的字符时,利用reduce的滚雪球特性 拿到最后的info.a的值 
            const value = _key.split('.').reduce((newObj, k) => newObj[k], this.data)
            node.parentNode.innerText = value || undefined
          }
        }

      }
      // 如果是input框
      if (node.nodeType === 1 && node.tagName === 'INPUT') {
        console.log(node, 'input');
        const _vModel = node.getAttribute('v-model')
        if (_vModel) {
          // 和data绑定 在input中显示data的数值
          let value = _vModel.split('.').reduce((newObj, k) => newObj[k], this.data)
          node.value = value
          node.addEventListener('keyup', this.handleInput.bind(this, _vModel, node), false)
        }
      }
      // 如果有子节点就继续递归
      node.childNodes && this.Compile(node)

    });
  }
  // input和data绑定
  handleInput(key, input) {
    const _value = input.value
    this.data[key] = _value
    console.log(key, _value);
  }

}

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <div>
      <input type="text" v-model="name" placeholder="姓名">
      <input type="text" v-model="age" placeholder="年龄">
      <input type="text" v-model="email" placeholder="邮箱">
      <input type="text" v-model="tel" placeholder="电话">
      <input type="text" v-model="info.a" placeholder="info.a">
    </div>
    <div>
      <p>
      </p>
      <p> 姓名:<span>{{name}}</span></p>
      <p> 年龄:<span>{{age}}</span></p>
      <p> 邮箱:<span>{{email}}</span></p>
      <p> info.a:<span>{{info.a}}</span></p>
      <p>
        <p>
          电话: <span>{{tel}}</span>
        </p>
      </p>
    </div>
  </div>
</body>

</html>
<script src="./index.js"></script>
<script>
  const vm = new Vue('#app', {
    name: 'ruirui',
    age: '1',
    email: '@qq.com ',
    tel: '22',
    info: {
      a: 1,
      b: '123'
    }
  })
</script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值