用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 相关文章推荐
基于jquery的实现简单的表格中增加或删除下一行
Aug 01 Javascript
jquery判断字符输入个数(数字英文长度记为1,中文记为2,超过长度自动截取)
Oct 15 Javascript
基于jquery完美拖拽,可返回拖动轨迹
Mar 29 Javascript
js获取input标签的输入值实现代码
Aug 05 Javascript
javascript中负数算术右移、逻辑右移的奥秘探索
Oct 17 Javascript
jQery使网页在显示器上居中显示适用于任何分辨率
Jun 09 Javascript
javascript生成大小写字母
Jul 03 Javascript
基于js里调用函数时,函数名带括号和不带括号的区别
Jul 28 Javascript
jQuery网页定位导航特效实现方法
Dec 19 Javascript
100行代码理解和分析vue2.0响应式架构
Mar 09 Javascript
vue使用xe-utils函数库的具体方法
Mar 06 Javascript
JS实现百度搜索框
Feb 25 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
全国FM电台频率大全 - 14 江西省
2020/03/11 无线电
JS与PHP向函数传递可变参数的区别实例代码
2011/05/18 PHP
php的sprintf函数的用法 控制浮点数格式
2014/02/14 PHP
正则表达式搭配js轻松处理json文本方便而老古
2013/02/17 Javascript
node.js中的fs.existsSync方法使用说明
2014/12/17 Javascript
jQuery中常用的遍历函数用法实例总结
2015/09/01 Javascript
JQUERY表单暂存功能插件分享
2016/02/23 Javascript
JS实现获取当前URL和来源URL的方法
2016/08/24 Javascript
AngularJS 实现弹性盒子布局的方法
2016/08/30 Javascript
javascript实现二叉树遍历的代码
2017/06/08 Javascript
js es6系列教程 - 新的类语法实战选项卡(详解)
2017/09/02 Javascript
node.js中TCP Socket多进程间的消息推送示例详解
2018/07/10 Javascript
vue动态改变背景图片demo分享
2018/09/13 Javascript
小程序组件之自定义顶部导航实例
2019/06/12 Javascript
vue-cli3访问public文件夹静态资源报错的解决方式
2020/09/02 Javascript
JS绘图Flot如何实现可选显示曲线图功能
2020/10/16 Javascript
原生JS实现拖拽效果
2020/12/04 Javascript
微信小程序实现下拉加载更多商品
2020/12/29 Javascript
Python urllib、urllib2、httplib抓取网页代码实例
2015/05/09 Python
Python获取邮件地址的方法
2015/07/10 Python
python之DataFrame实现excel合并单元格
2021/02/22 Python
使用Python写一个小游戏
2018/04/02 Python
Pycharm2017版本设置启动时默认自动打开项目的方法
2018/10/29 Python
cookies应对python反爬虫知识点详解
2020/11/25 Python
澳大利亚时尚前卫设计师珠宝在线:Amber Sceats
2017/10/04 全球购物
世界上最好的足球商店:Unisport
2019/03/02 全球购物
德国大型和小型家用电器网上商店:Energeto
2019/05/15 全球购物
Oasis服装官网:时尚女装在线
2020/07/09 全球购物
颇特女士香港官网:NET-A-PORTER香港
2021/03/08 全球购物
村主任群众路线个人对照检查材料
2014/09/26 职场文书
推荐信范文大全
2015/03/27 职场文书
golang特有程序结构入门教程
2021/06/02 Python
MongoDB数据库常用的10条操作命令
2021/06/18 MongoDB
原型和原型链 prototype和proto的区别详情
2021/11/02 Javascript
如何在python中实现ECDSA你知道吗
2021/11/23 Python
Go并发4种方法简明讲解
2022/04/06 Golang