用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 相关文章推荐
新页面打开实际尺寸的图片
Aug 25 Javascript
JS Range HTML文档/文字内容选中、库及应用介绍
May 12 Javascript
读jQuery之十二 删除事件核心方法
Jul 31 Javascript
网页编辑器ckeditor和ckfinder配置步骤分享
May 24 Javascript
Javascript中 关于prototype属性实现继承的原理图
Apr 16 Javascript
jQuery中attr()方法用法实例
Jan 05 Javascript
基于javascript html5实现多文件上传
Mar 03 Javascript
jqueryMobile 动态添加元素,展示刷新视图的实现方法
May 28 Javascript
基于BootStrap栅格栏系统完成网站底部版权信息区
Dec 23 Javascript
解决layui中table异步数据请求不支持自定义返回数据格式的问题
Aug 19 Javascript
vue项目中使用Hbuilder打包app 设置沉浸式状态栏的方法
Oct 22 Javascript
使用layer.msg 时间设置不起作用的解决方法
Sep 12 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
Home Coffee Roasting
2021/03/03 咖啡文化
php学习笔记 面向对象中[接口]与[多态性]的应用
2011/06/16 PHP
PHP CURL模拟登录新浪微博抓取页面内容 基于EaglePHP框架开发
2012/01/16 PHP
超详细的php用户注册页面填写信息完整实例(附源码)
2015/11/17 PHP
zend framework重定向方法小结
2016/05/28 PHP
js常用排序实现代码
2010/12/28 Javascript
JavaScript中变量提升 Hoisting
2012/07/03 Javascript
JS 使用for循环遍历子节点查找元素
2014/09/06 Javascript
Google Maps API地图应用示例分享
2014/10/23 Javascript
jQuery 判断图片是否加载完成方法汇总
2015/08/10 Javascript
BootStrap glyphicons 字体图标实现方法
2016/05/01 Javascript
详解angular element()方法使用
2017/04/08 Javascript
nodejs 图解express+supervisor+ejs的用法(推荐)
2017/09/08 NodeJs
ES6中新增的Object.assign()方法详解
2017/09/22 Javascript
微信小程序实现图片上传功能实例(前端+PHP后端)
2018/01/10 Javascript
vue和react等项目中更简单的实现展开收起更多等效果示例
2018/02/22 Javascript
基于mpvue小程序使用echarts画折线图的方法示例
2019/04/24 Javascript
详解vue 图片上传功能
2019/04/30 Javascript
JQuery获取元素尺寸、位置及页面滚动事件应用示例
2019/05/14 jQuery
微信小程序HTTP请求从0到1封装
2019/09/09 Javascript
vue 图片裁剪上传组件的实现
2020/11/12 Javascript
一个计算身份证号码校验位的Python小程序
2014/08/15 Python
简单介绍Python中的len()函数的使用
2015/04/07 Python
Django实现学生管理系统
2019/02/26 Python
Python Numpy库datetime类型的处理详解
2019/07/13 Python
基于Numba提高python运行效率过程解析
2020/03/02 Python
后端开发使用pycharm的技巧(推荐)
2020/03/27 Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
2020/04/26 Python
python使用多线程查询数据库的实现示例
2020/08/17 Python
详解使用scrapy进行模拟登陆三种方式
2021/02/21 Python
HTML5 Canvas中绘制矩形实例
2015/01/01 HTML / CSS
公司财务经理岗位职责
2015/04/08 职场文书
2015年结对帮扶工作总结
2015/05/04 职场文书
胡桃夹子观后感
2015/06/11 职场文书
2016秋季运动会前导词
2015/11/25 职场文书
MySQL分区路径子分区再分区
2022/04/13 MySQL