欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

用ES6的class模仿Vue写一个双向绑定的示例代码

程序员文章站 2022-04-20 13:35:10
本文介绍了用es6的class模仿vue写一个双向绑定的示例代码,分享给大家,具体如下: 最终效果如下: 构造器(constructor) 构造一个tin...

本文介绍了用es6的class模仿vue写一个双向绑定的示例代码,分享给大家,具体如下:

最终效果如下:

用ES6的class模仿Vue写一个双向绑定的示例代码

构造器(constructor)

构造一个tinyvue对象,包含基本的el,data,methods

class tinyvue{
 constructor({el, data, methods}){
  this.$data = data
  this.$el = document.queryselector(el)
  this.$methods = methods
  // 初始化
  this._compile()
  this._updater()
  this._watcher()
 }
}

编译器(compile)

用于解析绑定到输入框和下拉框的v-model和元素的点击事件@click。

先创建一个函数用来载入事件:

// el为元素tagname,attr为元素属性(v-model,@click)
_initevents(el, attr, callback) {
 this.$el.queryselectorall(el).foreach(i => {
  if(i.hasattribute(attr)) {
   let key = i.getattribute(attr)
   callback(i, key)
  }
 })
}

载入输入框事件

this._initevents('input, textarea', 'v-model', (i, key) => {
 i.addeventlistener('input', () => {
  object.assign(this.$data, {[key]: i.value})
 })
})

载入选择框事件

this._initevents('select', 'v-model', (i, key) => {
 i.addeventlistener('change', () => object.assign(this.$data, {[key]: i.options[i.options.selectedindex].value}))
})

载入点击事件

点击事件对应的是methods中的事件

this._initevents('*', '@click', (i, key) => {
 i.addeventlistener('click', () => this.$methods[key].bind(this.$data)())
})

视图更新器(updater)

同理先创建公共函数来处理不同元素中的视图,包括input、textarea的value,select的选择值,div的innerhtml

_initview(el, attr, callback) {
 this.$el.queryselectorall(el, attr, callback).foreach(i => {
  if(i.hasattribute(attr)) {
   let key = i.getattribute(attr),
    data = this.$data[key]
   callback(i, key, data)
  }
 })
}

更新输入框视图

this._initview('input, textarea', 'v-model', (i, key, data) => {
 i.value = data
})

更新选择框视图

this._initview('select', 'v-model', (i, key, data) => {
 i.queryselectorall('option').foreach(v => {
  if(v.value == data) v.setattribute('selected', true)
  else v.removeattribute('selected')
 })
})

更新innerhtml

这里实现方法有点low,仅想到正则替换{{text}}

let regexpinner = /\{{ *([\w_\-]+) *\}}/g
this.$el.queryselectorall("*").foreach(i => {
 let replacelist = i.innerhtml.match(regexpinner) || (i.hasattribute('vueid') && i.getattribute('vueid').match(regexpinner))
 if(replacelist) {
  if(!i.hasattribute('vueid')) {
   i.setattribute('vueid', i.innerhtml)
  }
  i.innerhtml = i.getattribute('vueid')
  replacelist.foreach(v => {
   let key = v.slice(2, v.length - 2)
   i.innerhtml = i.innerhtml.replace(v, this.$data[key])
  })
 }
})

监听器(watcher)

数据变化之后更新视图

<div id="app">
 <input type="text" v-model="text1"><br>
 <input type="text" v-model="text2"><br>
 <textarea type="text" v-model="text3"></textarea><br>
 <button @click="add">加一</button>
 <h1>您输入的是:{{text1}}+{{text2}}+{{text3}}</h1>
 <select v-model="select">
  <option value="volvo">volvo</option>
  <option value="saab">saab</option>
 </select>
 <select v-model="select">
  <option value="volvo">volvo</option>
  <option value="saab">saab</option>
 </select>
 <h1>您选择了:{{select}}</h1>
</div>
<script src="./tinyvue.js"></script>
<script>
 let app = new tinyvue({
  el: '#app',
  data: {
   text1: 123,
   text2: 456,
   text3: '文本框',
   select: 'saab'
  },
  methods: {
   add() {
    this.text1 ++
    this.text2 ++
   }
  }
 })
</script>

tinyvue全部代码

class tinyvue{
 constructor({el, data, methods}){
  this.$data = data
  this.$el = document.queryselector(el)
  this.$methods = methods
  this._compile()
  this._updater()
  this._watcher()
 }
 _watcher(data = this.$data) {
  let that = this
  object.keys(data).foreach(i => {
   let value = data[i]
   object.defineproperty(data, i, {
    enumerable: true,
    configurable: true,
    get: function () {
     return value;
    },
    set: function (newval) {
     if (value !== newval) {
      value = newval;
      that._updater()
     }
    }
   })
  })
 }
 _initevents(el, attr, callback) {
  this.$el.queryselectorall(el).foreach(i => {
   if(i.hasattribute(attr)) {
    let key = i.getattribute(attr)
    callback(i, key)
   }
  })
 }
 _initview(el, attr, callback) {
  this.$el.queryselectorall(el, attr, callback).foreach(i => {
   if(i.hasattribute(attr)) {
    let key = i.getattribute(attr),
     data = this.$data[key]
    callback(i, key, data)
   }
  })
 }
 _updater() {
  this._initview('input, textarea', 'v-model', (i, key, data) => {
   i.value = data
  })
  this._initview('select', 'v-model', (i, key, data) => {
   i.queryselectorall('option').foreach(v => {
    if(v.value == data) v.setattribute('selected', true)
    else v.removeattribute('selected')
   })
  })
  let regexpinner = /\{{ *([\w_\-]+) *\}}/g
  this.$el.queryselectorall("*").foreach(i => {
   let replacelist = i.innerhtml.match(regexpinner) || (i.hasattribute('vueid') && i.getattribute('vueid').match(regexpinner))
   if(replacelist) {
    if(!i.hasattribute('vueid')) {
     i.setattribute('vueid', i.innerhtml)
    }
    i.innerhtml = i.getattribute('vueid')
    replacelist.foreach(v => {
     let key = v.slice(2, v.length - 2)
     i.innerhtml = i.innerhtml.replace(v, this.$data[key])
    })
   }
  })
 }
 _compile() {
  this._initevents('*', '@click', (i, key) => {
   i.addeventlistener('click', () => this.$methods[key].bind(this.$data)())
  })
  this._initevents('input, textarea', 'v-model', (i, key) => {
   i.addeventlistener('input', () => {
    object.assign(this.$data, {[key]: i.value})
   })
  })
  this._initevents('select', 'v-model', (i, key) => {
   i.addeventlistener('change', () => object.assign(this.$data, {[key]: i.options[i.options.selectedindex].value}))
  })
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。