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


Posted in Javascript onApril 20, 2018

本文介绍了用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}))
  })
 }
}

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

Javascript 相关文章推荐
js截取函数(indexOf,join等)
Sep 01 Javascript
JS时间选择器 兼容IE6,7,8,9
Jun 26 Javascript
基于JavaScript实现继承机制之调用call()与apply()的方法详解
May 07 Javascript
js展开闭合效果演示代码
Jul 24 Javascript
Jquery动态替换div内容及动态展示的方法
Jan 23 Javascript
浅谈javascript中return语句
Jul 15 Javascript
js检查是否关闭浏览器的方法
Aug 02 Javascript
Angular中封装fancyBox(图片预览)遇到问题小结
Sep 01 Javascript
ionic3+Angular4实现接口请求及本地json文件读取示例
Oct 11 Javascript
解决在vue项目中,发版之后,背景图片报错,路径不对的问题
Mar 06 Javascript
解决vue-router进行build无法正常显示路由页面的问题
Mar 06 Javascript
详解vue后台系统登录态管理
Apr 02 Javascript
Vue写一个简单的倒计时按钮功能
Apr 20 #Javascript
使用Vue如何写一个双向数据绑定(面试常见)
Apr 20 #Javascript
Vue中如何实现proxy代理
Apr 20 #Javascript
React diff算法的实现示例
Apr 20 #Javascript
vue中子组件向父组件传递数据的实例代码(实现加减功能)
Apr 20 #Javascript
node实现登录图片验证码的示例代码
Apr 20 #Javascript
vue项目中api接口管理总结
Apr 20 #Javascript
You might like
根据key删除数组中指定的元素实现方法
2017/03/02 PHP
PHP设计模式之工厂模式实例总结
2017/09/01 PHP
window.open的功能全解析
2006/10/10 Javascript
纯CSS打造的导航菜单(附jquery版)
2010/08/07 Javascript
理解Javascript_02_理解undefined和null
2010/10/11 Javascript
js读取cookie方法总结
2014/10/31 Javascript
详解JavaScript的另类写法
2016/04/11 Javascript
浅析jQuery中使用$所引发的问题
2016/05/29 Javascript
微信小程序技巧之show内容展示,上传文件编码问题
2017/01/23 Javascript
js+html制作简单验证码
2017/02/16 Javascript
javascript实现非常简单的小数取整功能示例
2017/06/13 Javascript
浅谈angular4实际项目搭建总结
2017/12/01 Javascript
vue实现组件之间传值功能示例
2018/07/13 Javascript
在element-ui的select下拉框加上滚动加载
2019/04/18 Javascript
Vue 2.0 中依赖注入 provide/inject组合实战
2019/06/20 Javascript
django写用户登录判定并跳转制定页面的实例
2019/08/21 Python
pygame实现打字游戏
2021/02/19 Python
python序列化与数据持久化实例详解
2019/12/20 Python
python如何发送带有附件、正文为HTML的邮件
2021/02/27 Python
Sport-Thieme荷兰:购买体育用品
2019/08/25 全球购物
ZWILLING双立人法国网上商店:德国刀具锅具厨具品牌
2019/08/28 全球购物
维德科技C#面试题笔试题
2015/12/09 面试题
中英文自我评价常用句型
2013/12/19 职场文书
周年庆典邀请函范文
2014/01/23 职场文书
小学综治宣传月活动总结
2014/07/02 职场文书
民族团结演讲稿范文
2014/08/27 职场文书
党的群众路线对照检查材料范文
2014/09/24 职场文书
销售员未完成销售业绩的检讨书
2014/10/12 职场文书
店铺转让协议书
2014/12/02 职场文书
2015年安全生产目标责任书
2015/01/29 职场文书
书法社团活动总结
2015/05/07 职场文书
2015年幼儿园班主任工作总结
2015/05/12 职场文书
实习证明模板
2015/06/16 职场文书
三严三实学习心得体会(精选N篇)
2016/01/05 职场文书
Java基于Dijkstra算法实现校园导游程序
2022/03/17 Java/Android
「约定的梦幻岛」作画发布诺曼生日新绘
2022/03/21 日漫