实现一个双向数据绑定

实现如下图所示的双向数据绑定的案例

 

 代码如下:

<!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">
    <input type="text" v-model="username" placeholder="请输入用户名">
    <input type="text" v-model="password" placeholder="请输入密码">
    <input type="text" v-model="age" placeholder="年龄">
    <input type="text" v-model="sex" placeholder="性别">
    
    <div class="content">
      <p>姓名:<span>{{ username }}</span></p>
      <p>密码:<span>{{ password }}</span></p>
      <p>年龄:<span>{{ age }}</span></p>
      <p>性别:<span>{{ sex }}</span></p>
    </div>
  </div>
  <script src="./index2.js"></script>
  <script>
    const vm = new BindData('#app', {
      username: '123',
      password: '',
      age: '',
      sex: '',
    }) 
    vm.init(); // 执行初始化函数
    console.log(vm);
  </script>
</body>
</html>
// 1. 绑定响应式数据
// 初始化数据
// 2. 数据变化 -> 视图跟新

class BindData  {
  constructor (el, data) {
    if (!el) throw new Error('error!');

    this.$el = document.querySelector(el)
    this._data = data;
    // 存放响应式数据
    this.data = {};
    // 保存初始化时 {{key}} 对应的节点
    this.domPool = {};
  }

  init () {
    // 1. 初始化响应式数据 
    this.initState();
    this.initDom();
  }

  // 掌管dom方法
  initDom () {
    this.initInput();
    this.initContent(this.$el);
  }

  initInput () {
    const el = this.$el;

    const allChilds = el.getElementsByTagName('input');

    for (let i = 0; i < allChilds.length; i++) {
      const item = allChilds[i];

      const vModel = item.getAttribute('v-model');
      item.value = this.data[vModel] || '';

      // 绑定事件
      this.bindInputEvent(item, vModel);
    }
  }

  initContent (el) {
    const allChilds = el.childNodes,
          data = this.data;

    [...allChilds].forEach((item) => {
      // 文本节点
      if (item.nodeType === 3) {
        const value = item.nodeValue;

        if (value && value.length > 0) {

          let key = value.match(/\{\{(.*?)\}\}/);

          if (key) {
            key = key[1].trim();
            // 有{{key}} 的节点
            item._initTemplate = value;

            this.domPool[key] = item;

            item.nodeValue = item.nodeValue.replace(/\{\{(.*?)\}\}/, data[key] || undefined);
          }
        } 
        
      } else {
        this.initContent(item);
      }
    })
  }

  // 绑定事件
  bindInputEvent (input, key) {
    const data = this.data;

    input.addEventListener ('input', function () {
      const value = this.value.trim();
      // 修改响应式数据
      data[key] = value;
    }, false) 
  }


  initState () {
    const vm = this,
          _data = this._data;
           // 存放响应式数据
    this.data = new Proxy(_data, this.handlerProxy())
  }


  // 代理配置
  handlerProxy () {
    const _self = this;
    const get = createGetter(),
         set = createSetter();

    function createGetter () {
      return function (target, key, reciver) {
        console.log('响应式数据读取', key);
        return Reflect.get(target, key);
      }
    }

    function createSetter () {
      return function (target, key, value, reciver) {
        console.log(`数据设置${key}`);
        const res = Reflect.set(target, key, value);

        // 数据变化
        _self.renderContent(key, value);

        return res;
      } 
    }
    return {
      get,
      set,
    }
  }


  renderContent (key, value) {
    const domPool = this.domPool;
    console.log(key, value);
    domPool[key].nodeValue = value;
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值