简单实现VUE2.0双向绑定

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>简单实现vue双向绑定</title></title>
  </head>
  <script>
    class Vue {
      constructor(options) {

        // 1,保存数据
        this.$options = options
        this.$data = options.data
        this.$el = options.el

        // 2,将data添加到响应式数据中
        new Observer(this.$data)

        // 3,代理this.$data的数据
        Object.keys(this.$data).forEach(key => {
          this._proxy(key)
        })

        // 4,处理el
        new Compiler(this.$el, this)
      }

      _proxy(key) {
        Object.defineProperty(this, key, {
          configurable: true,
          enumerable: true,
          set(newValue) {
            this.$data[key] = newValue
          },
          get() {
            return this.$data[key]
          }
        })
      }
    }

    class Observer {
      constructor(data) {
        this.data = data

        Object.keys(data).forEach(key => {
          this.defineReactive(this.data, key, data[key])
        })
      }

      defineReactive(data, key, val) {
        const dep = new Dep()
        Object.defineProperty(data, key, {
          enumerable: true,
          configurable: true,
          get() {
            if(Dep.target) {
              dep.addSub(Dep.target)
            }
            return val
          },
          set(newValue) {
            if(newValue === val) {
              return
            }
            val = newValue
            dep.notify()
          }
        })
      }

    }

    class Dep {
      constructor() {
        this.subs = []
      }

      addSub(sub) {
        this.subs.push(sub)
      }

      notify() {
        this.subs.forEach(sub => {
          sub.update()
        })
      }
    }

    class Watcher {
      constructor(node, name, vm) {
        this.node = node
        this.name = name 
        this.vm = vm
        Dep.target = this
        this.update()
        Dep.target = null
      }

      update() {
        if(this.node.nodeName === 'INPUT') {
          this.node.value = this.vm[this.name]
        } else {
          this.node.nodeValue = this.vm[this.name]
        }
      }
    }

    const reg = /\{\{(.*)\}\}/

    class Compiler {
      constructor(el, vm) {
        this.el = document.querySelector(el)
        this.vm = vm

        this.frag = this._createFragment()
        this.el.appendChild(this.frag)
      }

      _createFragment() {
        const frag = document.createDocumentFragment()

        let child
        while( child = this.el.firstChild ) {
          this._compile(child)
          frag.appendChild(child)
        }
        return frag
      }

      _compile(node) {
        console.dir(node)
        if( node.nodeType === 1 ) {
          const attrs = node.attributes
          if(attrs.hasOwnProperty('v-model')) {
            const name = attrs['v-model'].nodeValue
            if(node.nodeName === 'INPUT') {
              node.addEventListener('input', e => {
                this.vm[name] = e.target.value
              })
              new Watcher(node, name, this.vm)
            }
          }
        }

        if( node.nodeType === 3 ) {
          if(reg.test(node.nodeValue)) {
            const name = RegExp.$1.trim()
            new Watcher(node, name, this.vm)
          }
        }
      }

    }
  </script>
  <body>
    <div id="app">
      <input v-model="name"></input> 
      {{name}}
    </div>
  </body>
  <script>
    let app = new Vue({
      el: "#app",
      data: {
        name: 'Adam'
      }
    })
  </script>
  
</html>

代码与思路整理自B站老师ilovecoding的教程(补充了一点对input的处理)最全最新Vue、Vuejs教程,从入门到精通_哔哩哔哩_bilibili 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值