用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动态调整iframe高度的代码
Apr 10 Javascript
用jQuery简化JavaScript开发分析
Feb 19 Javascript
javascript Demo模态窗口
Dec 06 Javascript
js中的for如何实现foreach中的遍历
May 31 Javascript
原生JS和jQuery版实现文件上传功能
Apr 18 Javascript
Websocket协议详解及简单实例代码
Dec 12 Javascript
JS判断鼠标进入容器的方向与window.open新窗口被拦截的问题
Dec 23 Javascript
@ResponseBody 和 @RequestBody 注解的区别
Mar 08 Javascript
node前端开发模板引擎Jade的入门
May 11 Javascript
JS实现的贪吃蛇游戏完整实例
Jan 18 Javascript
解决antd 下拉框 input [defaultValue] 的值的问题
Oct 31 Javascript
一篇文章学会Vue中间件管道
Jun 20 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
在smarty中调用php内置函数的方法
2013/02/07 PHP
php中防止SQL注入的最佳解决方法
2013/04/25 PHP
使用PHP生成二维码的方法汇总
2015/07/22 PHP
PHP二维索引数组的遍历实例分析【2种方式】
2019/06/24 PHP
javaScript - 如何引入js代码
2021/03/09 Javascript
Mootools 1.2教程 Fx.Tween的使用
2009/09/15 Javascript
基于jquery的监控数据是否发生改变
2011/04/11 Javascript
jQuery动态地获取系统时间实现代码
2013/05/24 Javascript
举例详解Python中smtplib模块处理电子邮件的使用
2015/06/24 Javascript
jQuery添加和删除指定标签的方法
2015/12/16 Javascript
JavaScript基本语法学习教程
2016/01/14 Javascript
jQuery链式调用与show知识浅析
2016/05/11 Javascript
js中用cssText设置css样式的简单方法
2016/09/19 Javascript
JavaScript字符串检索字符的方法
2017/06/23 Javascript
vue-router相关基础知识及工作原理
2018/03/16 Javascript
解决npm管理员身份install时出现权限的问题
2018/03/16 Javascript
js实现简单放大镜效果
2020/03/07 Javascript
[01:05]主宰至宝剑心之遗
2017/03/16 DOTA
Python素数检测的方法
2015/05/11 Python
Python中将字典转换为XML以及相关的命名空间解析
2015/10/15 Python
Python基础之文件读取的讲解
2019/02/16 Python
Python3+OpenCV2实现图像的几何变换(平移、镜像、缩放、旋转、仿射)
2019/05/13 Python
Django实现文件上传下载
2019/10/06 Python
Pytorch .pth权重文件的使用解析
2020/02/14 Python
使用phonegap创建联系人的实现方法
2017/03/30 HTML / CSS
固特异美国在线轮胎店:Goodyear Tire
2019/02/23 全球购物
.net面试题
2016/09/17 面试题
好的促销活动方案
2014/08/21 职场文书
入党积极分子对十八届四中全会期盼的思想汇报
2014/10/17 职场文书
个人整改措施书面材料
2014/10/24 职场文书
出纳工作检讨书范文
2014/12/27 职场文书
《我们的民族小学》教学反思
2016/02/19 职场文书
使用nginx配置访问wgcloud的方法
2021/06/26 Servers
Python Django模型详解
2021/10/05 Python
MySQL数据库如何使用Shell进行连接
2022/04/12 MySQL
Python开发简易五子棋小游戏
2022/05/02 Python