用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 相关文章推荐
JavaScript常用脚本汇总(一)
Mar 04 Javascript
很棒的js选项卡切换效果
Jul 15 Javascript
vue.js单页面应用实例的简单实现
Apr 10 Javascript
bootstrap模态框嵌套、tabindex属性、去除阴影的示例代码
Oct 17 Javascript
js与jQuery实现的用户注册协议倒计时功能实例【三种方法】
Nov 09 jQuery
解决Vue不能检测数组或对象变动的问题
Feb 24 Javascript
elementUI多选框反选的实现代码
Apr 03 Javascript
vue router 跳转时打开新页面的示例方法
Jul 28 Javascript
基于node+vue实现简单的WebSocket聊天功能
Feb 01 Javascript
JS+DIV实现拖动效果
Feb 11 Javascript
Vue时间轴 vue-light-timeline的用法说明
Oct 29 Javascript
vue实现可移动的悬浮按钮
Mar 04 Vue.js
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
PHP检测数据类型的几种方法(总结)
2017/03/04 PHP
php lcg_value与mt_rand生成0~1随机小数的效果对比分析
2017/04/05 PHP
Laravel框架中Blade模板的用法示例
2017/08/30 PHP
php使用gearman进行任务分发操作实例详解
2020/02/26 PHP
javascript 选择文件夹对话框(web)
2009/07/07 Javascript
jQuery 1.5最新版本的改进细节分析
2011/01/19 Javascript
javascript学习(一)构建自己的JS库
2013/01/02 Javascript
Javascript学习笔记之函数篇(四):arguments 对象
2014/11/23 Javascript
JS实现横向拉伸动感伸缩菜单效果代码
2015/09/04 Javascript
JavaScript html5 canvas画布中删除一个块区域的方法
2016/01/26 Javascript
js实现数组冒泡排序、快速排序原理
2016/03/08 Javascript
基于javascript实现图片切换效果
2016/04/17 Javascript
浅析前端路由简介以及vue-router实现原理
2018/06/01 Javascript
Node.js 实现简单的无侵入式缓存框架的方法
2019/07/21 Javascript
OpenLayers3实现图层控件功能
2020/09/25 Javascript
[02:17]《辉夜杯》TRG战队巡礼
2015/10/26 DOTA
Python获取linux主机ip的简单实现方法
2016/04/18 Python
用python找出那些被“标记”的照片
2017/04/20 Python
分享一个简单的python读写文件脚本
2017/11/25 Python
python email smtplib模块发送邮件代码实例
2018/04/26 Python
python自动化生成IOS的图标
2018/11/13 Python
Python3 Tkinter选择路径功能的实现方法
2019/06/14 Python
浅谈PySpark SQL 相关知识介绍
2019/06/14 Python
Python使用pyautocad+openpyxl处理cad文件示例
2019/07/11 Python
解决Python计算矩阵乘向量,矩阵乘实数的一些小错误
2019/08/26 Python
关于pytorch处理类别不平衡的问题
2019/12/31 Python
在PyTorch中使用标签平滑正则化的问题
2020/04/03 Python
纯CSS3实现漂亮的input输入框动画样式库(Text input love)
2018/12/29 HTML / CSS
大学生年度自我鉴定
2013/10/31 职场文书
社区党务公开实施方案
2014/03/18 职场文书
春节请假条
2014/04/11 职场文书
设立有限责任公司出资协议书
2014/11/01 职场文书
2014年便民服务中心工作总结
2014/12/20 职场文书
音乐教师个人总结
2015/02/06 职场文书
2015年学校安全工作总结
2015/04/22 职场文书
JAVA 线程池(池化技术)的实现原理
2022/04/28 Java/Android