用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 相关文章推荐
EasyUI的treegrid组件动态加载数据问题的解决办法
Dec 11 Javascript
Jqgrid表格随窗口大小改变而改变的简单实例
Dec 28 Javascript
深入理解javascript的执行顺序
Apr 04 Javascript
js+css实现上下翻页相册代码分享
Aug 18 Javascript
编写高性能Javascript代码的N条建议
Oct 12 Javascript
jquery移动端TAB触屏切换实现效果
Dec 22 Javascript
javascript 判断页面访问方式电脑或者移动端
Sep 19 Javascript
AngularJS用户选择器指令实例分析
Nov 04 Javascript
Angular使用过滤器uppercase/lowercase实现字母大小写转换功能示例
Mar 27 Javascript
详解vue的diff算法原理
May 20 Javascript
vue 点击展开显示更多(点击收起部分隐藏)
Apr 09 Javascript
Vue实现根据hash高亮选项卡
May 27 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
PHP substr 截取字符串出现乱码问题解决方法[utf8与gb2312]
2011/12/16 PHP
php中自定义函数dump查看数组信息类似var_dump
2014/01/27 PHP
php写入数据到CSV文件的方法
2015/03/14 PHP
PHP序列化/对象注入漏洞分析
2016/04/18 PHP
CI框架实现cookie登陆的方法详解
2016/05/18 PHP
[原创]网络复制内容时常用的正则+editplus
2006/11/30 Javascript
juqery 学习之四 筛选过滤
2010/11/30 Javascript
javascript实用方法总结
2015/02/06 Javascript
JavaScript统计网站访问次数的实现代码
2015/11/18 Javascript
jQuery解析Json实例详解
2015/11/24 Javascript
js日期插件dateHelp获取本月、三个月、今年的日期
2016/03/07 Javascript
jQuery改变form表单的action,并进行提交的实现代码
2016/05/25 Javascript
用NodeJS实现批量查询地理位置的经纬度接口
2016/08/16 NodeJs
原生js实现无缝轮播图效果
2017/01/11 Javascript
Angularjs处理页面闪烁的解决方法
2017/03/09 Javascript
详解如何构建Angular项目目录结构
2017/07/13 Javascript
浅谈React组件之性能优化
2018/03/02 Javascript
移动端滑动切换组件封装 vue-swiper-router实例详解
2018/11/25 Javascript
JS浮点数运算结果不精确的Bug解决
2019/08/01 Javascript
js点击事件的执行过程实例分析【冒泡与捕获】
2020/04/11 Javascript
[50:34]VGJ.T vs Fnatic 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
Python 基础教程之包和类的用法
2017/02/23 Python
浅谈python中的__init__、__new__和__call__方法
2017/07/18 Python
padas 生成excel 增加sheet表的实例
2018/12/11 Python
Python实现元素等待代码实例
2019/11/11 Python
Python3爬虫里关于代理的设置总结
2020/07/30 Python
css3动画 小球滚动 js控制动画暂停
2019/11/29 HTML / CSS
HTML5 常用语法一览(列举不支持的属性)
2010/01/26 HTML / CSS
加拿大在线隐形眼镜专家:PerfectLens.ca
2016/11/19 全球购物
锐步美国官方网站:Reebok美国
2018/01/10 全球购物
竞选部门副经理的自荐书范文
2014/02/11 职场文书
中学生旷课检讨书2篇
2014/10/09 职场文书
教师个人工作总结范文2014
2014/11/10 职场文书
公司行政助理岗位职责
2015/04/11 职场文书
2015年大学生工作总结
2015/04/21 职场文书
工作报告范文
2019/06/20 职场文书