js实现简单的v-model,v-if,v-show,点击事件,双项数据绑定

class Vue {
  constructor(options = {}) {
    this.$el = document.querySelector(options.el)
    let data = this.data = options.data
    Object.keys(data).forEach(key => {
      this.proxyData(key)
    })
    this.methods = options.methods //事件方法
    this.watcherTask = {} //需要监听的任务列表
    this.instrList = {}
    this.observer(data) // 初始化劫持监听所有数据
    this.compile(this.$el) // 解析dom
  }
  proxyData(key) {
    let that = this
    Object.defineProperty(this, key, {
      configurable: false,
      enumerable: true,
      get: () => {
        return this.data[key]
      },
      set: (newValue) => {
        return that.data[key] = newValue;
      }
    })
  }
  observer(data) {
    let that = this
    // data是一个message:'张三'
    Object.keys(data).forEach(ele => {
      // 这样拿到data中key值
      let value = data[ele]
      // console.log(value)
      this.watcherTask[ele] = [] // 转为空数组
      // console.log(this.watcherTask)
      Object.defineProperty(data, ele, {
        configurable: false,
        enumerable: true,
        get: () => {
          return value
        },
        set: (newValue) => {
          // console.log(newValue)
          if (newValue !== value) {
            value = newValue
            that.watcherTask[ele].forEach(result => {
              result.update()
            })
          }
        }
      })
    })
  }
  compile(el) {
    let nodes = el.childNodes //childNodes 属性返回节点的子节点集合,以 NodeList 对象。
    for (let i = 0; i < nodes.length; i++) {
      let node = nodes[i]
      if (node.nodeType === 3) { // 判断是不是文本节点
        let text = node.textContent.trim() // 除掉首位空格
        if (!text) continue
        this.compileText(node, 'textContent')
      } else if (node.nodeType === 1) {
        if (node.childNodes.length > 0) {
          this.compile(node)
        }
        // 原生中间包含某个值hasAttribute 元素的标签是等于某一个值tagName
        if (node.hasAttribute('v-model') && node.tagName === "INPUT" || node.tagName === "TEXTARER") {
          // 自执行函数
          node.addEventListener('input', (() => {
            // getAttribute 获取标签上的某个属性的值
            let attrVal = node.getAttribute('v-model')
            this.watcherTask[attrVal].push(new Watcher(node, this, attrVal, 'value'))
            node.removeAttribute('v-model')
            return () => {
              this.data[attrVal] = node.value
            }
          })())
        }
        if (node.hasAttribute('v-html')) {
          let attrNewVal = node.getAttribute('v-html')
          this.watcherTask[attrNewVal].push(new Watcher(node, this, attrNewVal, 'innerHTML'))
          node.removeAttribute('v-html')
        }
        this.compileText(node, 'innerHTML')
        if (node.hasAttribute('@click')) {
          let getbtnClick = node.getAttribute('@click')
          node.removeAttribute('@click')
          node.addEventListener('click', e => {
            //  this.methods[getbtnClick] 可有可无 防止点击报错的才加
            this.methods[getbtnClick] && this.methods[getbtnClick].bind(this)()
          })
        }
        if (node.hasAttribute('v-if')) {
          let newValue = this.data[node.getAttribute('v-if')]
          if (!newValue) {
            el.removeChild(node)
          }
          node.removeAttribute('v-if')
        }
        if (node.hasAttribute('v-show')) {
          let newValue = this.data[node.getAttribute('v-show')]
          if (!newValue) {
            node.style.cssText = "display:none"
          }
          node.removeAttribute('v-show')
        }
      }
    }
  }
  compileText(node, type) {
    let reg = /\{\{(.*?)\}\}/g
    let txt = node.textContent
    if (reg.test(txt)) {
      node.textContent = txt.replace(reg, (matched, value) => { // 解析{{}}语法
        let newValue = value.trim()
        let tlp = this.watcherTask[newValue] || []
        tlp.push(new Watcher(node, this, newValue, type))
        if (newValue.split('.').length > 0) {
          let v = null
          newValue.split('.').forEach((val, i) => {
            v = !v ? this[val] : v[val]
          })
          return v
        } else {
          return this[val]
        }
      })
    }
  }
}
class Watcher {
  constructor(el, vm, value, type) {
    this.el = el
    this.vm = vm
    this.value = value
    this.type = type
    this.update()
  }
  update() {
    this.el[this.type] = this.vm.data[this.value]
  }
}

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
  </style>
</head>
<body>
  <div id="box">
    <span>{{massage}}</span>
    <input type="text" v-model="someText">
    <button @click='btnClick'>点击事件</button>
    <div>{{someText}}</div>
    <div v-if="condition" class="class1">{{someText}}</div>
    <div v-if="condition1" class="class2">
      <span>{{someText}}</span>
      <span>eqewe</span>
    </div>
    <div v-if="condition2" class="class3">{{someText}}</div>
    <div v-if="condition3" class="class4">{{someText}}</div>
    <div v-show="show">{{someText}}</div>
    <div v-if="heddin">{{someText}}</div>
  </div>

  <script src="./index.js"></script>
  <script>
    new Vue({
      el:'#box',
      data:{
        massage:"张三",
        someText:'',
        condition: false,
        condition3:false,
        condition2:true,
        condition1:false,
        show:false,
        heddin:true
      },
      methods: {
        btnClick(){
          this.massage = 'qqqqqqqqqqqqqqqq'
        }
      },
    })
  </script>
</body>
</html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值