VUE逐点突破系列 – VUE响应式原理剖析

手写实现简单的响应式双向绑定

<html>
  <head>
    <meta charset="UTF-8"/>
    <title>vue</title>
    <script src="./vue.js"></script>
  </head>
</html>
<body>
  <div id="app">
    {{name}}
    <div>{{message}}</div>
    <input v-model='test'/>
    {{test}}
  </div>
</body>
<script>
  let vm = new Vue({
  el: '#app',
  data: {
    name: 'lili',
    message: '测试数据',
    test: ''
  }
})
</script>

下面是html文件中引用的vue.js文件

继承类 EventTarget

class Vue extends EventTarget{
  constructor(option){
    super();
    this.option = option;
    this._data = this.option.data;
    this.el = document.querySelector(this.option.el);
    this.observe(this._data)
    this.compileNode(this.el);
  }
  observe(data){
    let _this = this
    this._data = new Proxy(data,{
      set(target,prop,newValue){
        let event = new CustomEvent(prop,{
          detail: newValue
        })
        _this.dispatchEvent(event)
        return Reflect.set(...arguments)
      }
    })
  }
  compileNode(el){
    let child = el.childNodes;
    [...child].forEach(node => {
      if(node.nodeType === 3){
      	// 文字节点
        let text = node.textContent;
        // 通过正则匹配两侧为{{}}且忽略中间空格的代码
        let reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/
        if(reg.test(text)){
          let $1 = RegExp.$1
          this._data[$1] && (node.textContent = text.replace(reg, this._data[$1]))
          this.addEventListener($1, e=>{
            node.textContent = text.replace(reg, e.detail)
          })
        }
      }else if(node.nodeType === 1){
      	// 元素节点
        let attr = node.attributes;
        // 元素节点含有v-model属性
        if(attr.hasOwnProperty('v-model')){
          let keyName = attr['v-model'].nodeValue;
          node.value = this._data[keyName]
          node.addEventListener('input',e=>{
            this._data[keyName] = node.value
          })
        }
        // 递归编译
        this.compileNode(node)
      }
    })
  }
}
Logo

前往低代码交流专区

更多推荐