都2025年了,面试官说你不会双向绑定就不要来面试!

1,103 阅读2分钟

前言

最近零食很忙出了一道上机题:给你30分钟,请实现数据的双向绑定!如下图

微信截图_20250508154625.png

机智的小伙马上就想到了解决办法。这种题还用半个小时?给我5分钟,我要开始装逼了~

<div id="app">
  <input type="text" v-model='name1' placeholder="请输入" />
  <input type="text" v-model='name2' placeholder="请输入" />
  <span>{{name1}}</span>
  <span>{{name2}}</span>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: '#app',
        data: {
            name1: "周杰伦",
            name2: "许嵩",
        },
    })
</script>

面试官:今天面试到此结束,根据你今天的表现!我还得跟部门领导商量一下!请回去等消息吧~

很显然面试的效果是达到了,可是完全没走心!什么叫走心呢?那就是不能提及尤雨溪但是还是要成为尤雨溪!

手动实现

1.创建一个类吧


  class VAE {
    constructor(el, data) {
      this.el = document.querySelector(el)
      this._data = data
      this.domPool = {}
      this.init()
    }
    //初始化
    init() {
      this.reactive()
      this.method()
      this.render(this.el)
    }
    //数据劫持
    reactive() {
     
    }
    //绑定方法
    method() {
     
    }
    //刷新dom
    render(el) {
      
    }
  }

2.数据劫持


  class VAE {
    constructor(el, data) {
      this.el = document.querySelector(el)
      this._data = data
      this.init()
    }
    //初始化
    init() {
     //省略...
    }
    //数据劫持
    reactive() {
     let _this = this
      this.data = {}
      for (let key in this._data) {
        Object.defineProperty(this.data, key, {
          get() {
            return _this._data[key]
          },
          set(val) {
            _this._data[key] = val
          }
        })
      }
    }

    method() {} //省略..
    render(el) {}//省略..
  }

3.绑定事件


  class VAE {
    constructor(el, data) {
      this.el = document.querySelector(el)
      this._data = data
      this.init()
    }
    //初始化
    init() {
     //省略...
    }
    //数据劫持
    reactive() {
     let _this = this
      this.data = {}
      for (let key in this._data) {
        Object.defineProperty(this.data, key, {
          get() {
            return _this._data[key]
          },
          set(val) {
            _this._data[key] = val
          }
        })
      }
    }
    //绑定事件
    method() {
      const onInput = document.querySelectorAll('input') 
      let _this = this
      onInput.forEach(input => {
        const vModel = input.getAttribute('v-model') //找到v-model属性
        input.value = _this.data[vModel]
        if (vModel) {
          input.addEventListener('keyup', function (e) {
            _this.data[vModel] = e.target.value //改变就重新赋值
          })
        }
      })
    } 
    render(el) {}//省略..
  }

4.渲染视图


  class VAE {
    constructor(el, data) {
      this.el = document.querySelector(el)
      this._data = data
      this.init()
    }
    //初始化
    init() {
     //省略...
    }
    //数据劫持
    reactive() {
     let _this = this
      this.data = {}
      for (let key in this._data) {
        Object.defineProperty(this.data, key, {
          get() {
            return _this._data[key]
          },
          set(val) {
            _this.domPool[key].innerText = val //一旦值改变就更新span标签内容 
           _this._data[key] = val
          }
        })
      }
    }
    //绑定事件
    method() {
      const onInput = document.querySelectorAll('input') 
      let _this = this
      onInput.forEach(input => {
        const vModel = input.getAttribute('v-model') //找到v-model属性
        input.value = _this.data[vModel]
        if (vModel) {
          input.addEventListener('keyup', function (e) {
            _this.data[vModel] = e.target.value //改变就重新赋值
          })
        }
      })
    } 
    render(el) {
      let childNodes = el.childNodes;
      childNodes.forEach(node => {
        if (node.nodeType == 3) {
          if (node.nodeValue.trim().length) {
            let key = node.nodeValue.match(/\{\{(.+?)\}\}/)[1].trim() //找到花括号里面的变量名
            this.domPool[key] = node.parentNode  //找到父元素节点,也就是span标签
            node.parentNode.innerText = this.data[key]  //初始化花括号的值
          }
        }
        node.childNodes && this.render(node)
      })
    }
  }

5.页面使用

  <div id="app">
    <input type="text" v-model="name1" placeholder="请输入" />
    <input type="text" v-model="name2" placeholder="请输入" />
    <br>
    <span>{{name1}}</span>
    <br>
    <span>{{name2}}</span>
  </div>
  
  <script>
        const app = new VAE('#app', {
            name1: '周杰伦',
            name2: '许嵩'
         })
  </script>

6.效果如下

QQ202558-161319.gif

总结:

其实理解双向绑定并不难,核心还是Object.defineProperty,唯一的一些细节处理是怎么关联dom! 实际的vue源码还涉及到了依赖收集。但是本篇文章不讲源码,就是单纯的实现一个效果!怎么样兄弟们,如果觉得这篇文章对你有帮助,就点赞+收藏吧!有问题欢迎留言~