用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)
数据变化之后更新视图
_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()
}
}
})
})
}
使用
<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}))
})
}
}
用ES6的class模仿Vue写一个双向绑定的更多相关文章
- 用vue写一个仿简书的轮播图
原文地址:用vue写一个仿简书的轮播图 先展示最终效果: Vue的理念是以数据驱动视图,所以拒绝通过改变元素的margin-top来实现滚动效果.写好css样式,只需改变每张图片的class即可实现轮 ...
- 用PHP写一个双向队列
PHP写一个双向队列,其实是在考察PHP几个内置数组的函数 用PHP写一个双向队列 <?php class Deque{ public $queue = array(); /** * 尾部入对 ...
- Vue的数据双向绑定和Object.defineProperty()
Vue是前端三大框架之一,也被很多人指责抄袭,说他的两个核心功能,一个数据双向绑定,一个组件化分别抄袭angular的数据双向绑定和react的组件化思想,咱们今天就不谈这种大是大非,当然我也没到达那 ...
- 原生js实现 vue的数据双向绑定
原生js实现一个简单的vue的数据双向绑定 vue是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时 ...
- Vue框架之双向绑定事件
Vue框架之双向绑定事件 首先介绍下Vue框架的语法 vue通过 {{temp}} 来渲染变量 {{count+100}} # 求和 v-text # 为标签插入text文本 v-html # 为标签 ...
- 剖析Vue原理&实现双向绑定MVVM-1
本文能帮你做什么?1.了解vue的双向数据绑定原理以及核心代码模块2.缓解好奇心的同时了解如何实现双向绑定为了便于说明原理与实现,本文相关代码主要摘自vue源码, 并进行了简化改造,相对较简陋,并未考 ...
- es6的set和get实现数据双向绑定,监听变量变化。
直接上代码吧,这个用法真的是效仿了.net的枚举. vue的数据双向绑定就是用这个实现的. 代码: html: <input type="text" id="inp ...
- 组件的通信 :provide / inject 对象进入后,就等于不用props,然后内部对象,直接复制可以接受数组,属性不能直接复制,可以用Object.assgin覆盖对象,或者Vue的set 双向绑定数据
组件的通信 :provide / inject 对象进入后,就等于不用props,然后内部对象,直接复制可以接受数组,属性不能直接复制,可以用Object.assgin覆盖对象,或者Vue的set 双 ...
- 用vue写一个仿app下拉刷新的组件
如果你用vue弄移动端的页面,那么下拉刷新还是比较常见的场景,下面来研究如何写一个下拉刷新的组件(先上图); 由于节省大家的时间,样式就不贴出来了. html结构也不必介绍了,直接看代码吧-.- &l ...
随机推荐
- DCloud-流应用:杂项
ylbtech-DCloud-流应用:杂项 1.返回顶部 2.返回顶部 3.返回顶部 4.返回顶部 5.返回顶部 6.返回顶部 7.返回顶部 8.返回顶部 9.返回 ...
- distinct可以用in代替(小技巧)
distinct可以用in代替,in的好处是直接能获取所有数据,而distunct只能获取distinct的字段,不过效率肯定高一些.
- centos6 安装teamviewer
wget http://www.teamviewer.com/download/teamviewer_linux.rpm yum -y install temviewerX.rpm 机器最好联网,能够 ...
- Android实现智能提示的文本输入框AutoCompleteTextView
今天我们要讲一个十分简单的内容,就是一个安卓控件的使用,用法很简单,但是很常用的一个.这里我用两种不同的写法来处理.当然,无论用哪一种写法,效果都是一样的. 我们先来看效果图. 要实现这种效果十分简单 ...
- js语法和数据类型
基础知识(Basics) JavaScript 的很多语法借鉴自 Java,但也受 Awk,Perl 和 Python 影响. JavaScript 是大小写敏感的,使用 Unicode 字符集. 在 ...
- 【总结整理】dojo学习
Dojo Toolkit 的特性可以分到 4 个不同部分.这种划分使得开发人员可以将库大小保持到最小,确保应用程序性能不受大量 JavaScript 库下载的影响.例如,如果您只需要 Ajax 支持性 ...
- css知多少(5)——选择器(转)
css知多少(5)——选择器 1. 引言 从本节开始,就进入本系列的第二个部分——css和html的结合——说白了就是选择器. CSS中定义了样式,如何将这些样式设置到相应的html节点上?就不得 ...
- php get_include_path();是干嘛的、??还有set_include_path();/?????
首先 我们来看这个全局变量:__FILE__ 它表示文件的完整路径(当然包括文件名在内) 也就是说它根据你文件所在的目录不同,有着不同的值:当然,当它用在包行文件中的时候,它的值是包含的路径: 然后: ...
- Excel VBA连接MySql 数据库获取数据
编写Excel VBA工具,连接并操作Mysql 数据库. 系统环境: OS:Win7 64位 英文版 Office 2010 32位 英文版 1.VBA连接MySql前的准备 Tools---> ...
- 2用java代码实现冒泡排序算法(转载)
import java.util.Scanner; public class Maopao { public static void main(String[] args) { System.out. ...