Vue2-data,v-model,@click部分原理

创建vue.js文件

class Vue {
  constructor(options) {
    // console.log(options)
    this.$options = options
    // 更新视图需要
    this.$watchEvent = {}
    if (typeof (options.beforeCreated == 'function')) {
      options.beforeCreated.bind(this)()
    }

    this.$data = options.data
    this.proxyData()
    this.observe()
    // console.log(this.$data)

    if (typeof (options.created == 'function')) {
      options.created.bind(this)()
    }
    if (typeof (options.beforeMount == 'function')) {
      options.beforeMount.bind(this)()
    }
    this.$el = document.querySelector(options.el)
    // console.log(this.$el)
    // 渲染节点
    this.compile(this.$el)
    if (typeof (options.mounted == 'function')) {
      options.mounted.bind(this)()
    }

  }
  // 给vue大对象赋属性,来自于data中
  // data中的属性值和Vue大对象的属性保持双向(劫持)
  // 保证前台this.str 能打印出来
  proxyData() {
    for (let key in this.$data) {
      Object.defineProperty(this, key, {
        get() {
          return this.$data[key]
        },
        set(val) {
          this.$data[key] = val
        }
      })
    }
  }
  // 触发data中的数据发生变化来执行watch中的update
  observe() {
    for (let key in this.$data) {
      let value = this.$data[key]
      let that = this
      Object.defineProperty(this.$data, key, {
        get() {
          return value
        },
        set(val) {
          value = val
          if (that.$watchEvent[key]) {
            that.$watchEvent[key].forEach((item, key) => {
              item.update()
            })
          }
        }
      })
    }
  }
  compile(node) {
    // console.log(node.childNodes)
    node.childNodes.forEach((item, index) => {
      // 1 是元素,3是文本
      // console.log(item.nodeType)
      if (item.nodeType == 1) {
        // console.log('aclikc', item.hasAttribute('@click'))
        if (item.hasAttribute('@click')) {
          // 判读是否是click事件
          let vmKey = item.getAttribute('@click').trim()  // @click绑定的属性名
          item.addEventListener('click', (event) => {
            this.eventFn = this.$options.methods[vmKey].bind(this)
            this.eventFn(event)
          })
        }
        // 判断元素节点是否添加了v-model
        if (item.hasAttribute('v-model')) {
          let vmKey = item.getAttribute('v-model').trim()
          if (this.hasOwnProperty(vmKey)) {
            // console.log(this, this[vmKey])
            item.value = this[vmKey]
          }
          // 实现input里面的值修改,data里面的值修改
          item.addEventListener('input', (event) => {
            this[vmKey] = item.value
          })
        }

        if (item.childNodes.length > 0) {
          this.compile(item)
        }
      }
      if (item.nodeType == 3) {
        // 正则匹配{{}}
        let reg = /\{\{(.*?)\}\}/g;
        let text = item.textContent
        item.textContent = text.replace(reg, (match, vmKey) => {
          console.log(vmKey)
          vmKey = vmKey.trim()
          // 判断data中是否
          if (this.hasOwnProperty(vmKey)) {
            // this  当前对象
            // item:当前节点
            let watch = new Watch(this, vmKey, item, 'textContent')
            if (this.$watchEvent[vmKey]) {
              this.$watchEvent[vmKey].push(watch)
            } else {
              this.$watchEvent[vmKey] = []
              this.$watchEvent[vmKey].push(watch)
            }
          }
          return this.$data[vmKey]
        })
      }
    })
  }
}

class Watch {
  constructor(vm, key, node, attr) {
    // 对象
    this.vm = vm
    // 属性名称
    this.key = key
    // 节点
    this.node = node
    // 改变文本节点内容的字符串
    this.attr = attr
  }
  // 执行改变(update)操作
  update() {
    this.node[this.attr] = this.vm[this.key]
  }
}

创建index.html文件

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    {{str}}
    <h1>{{str}}</h1>
    <button @click="btn">按钮</button>
    <p>{{b}}</p>
    <input type="text" v-model="str">
  </div>



  <script text="text/javascript" src="vue.js">

  </script>
  <script text="text/javascript">
    new Vue({
      el: '#app',
      data: {
        str: '你好',
        b: '这也是data'
      },
      beforeCreated() {
        console.log('beforeCreated', this.$el, this.$data)
      },
      created() {
        console.log('created', this.$el, this.$data)
      },
      beforeMount() {
        console.log('beforeMount', this.$el, this.$data)
      },
      mounted() {
        console.log('mounted', this.$el, this.$data)
      },
      methods: {
        btn(e) {
          this.str = '改变值'
          console.log(this, 'helo')
        }
      }
    })

  </script>
</body>

</html>

双向绑定原理

通过Object.defineProperty劫持数据发生的改变,如果数据发生了改变,(在set中进行赋值),触发update方法进行更新节点内容,从而实现了数据的双向绑定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值