作为MVVM的核心内容数据绑定,是非常要必要去探究和简单实现一下的。
Vue.js数据绑定功能
来看一下Vue.js的数据是如何使用的:1
2
3
4
5
6
7<div id="app">
<input v-model="message" />
</div>
<script type="text/javascript">
var message = 'Vue.js is rad';
new Vue({ el: '#app', data: { message } });
</script>
如果使用{ {} }
的话就会涉及到模板解析方面的知识,这里为求把焦点更集中,所以使用了input
和v-model
的配合。
首先可以看到,我们的input
标签是通过v-model
来绑定数据对象的,当数据对象message
内容发生改变的时候就会引起input
value的改变。
实现
HTML配置
先看看HTML配置1
2
3
4
5<div id="app">
<input s-model="message" />
<button id="click">设置message为Hello World</button>
<br/><span s-model="message"></span>
</div>
主要设置了一个绑定了message
数据对象的input
输入框,一个点击后会设置message
对象值为Hello world
的button
,还有一个绑定了message
数据对象的span
用来看看它的值的改变。
Proxy Handler
这里的object劫持采用了Proxy的方式,下面是handler的定义:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function BindingProxyHandler () {
return {
get: (target, key, receiver) => {
return Reflect.get(target, key, receiver);
},
set: (target, key, value, receiver) => {
// 搜索绑定key的model的DOM
let doms = document.querySelectorAll(`${this.elTag} [s-model="${key}"]`) || null
if (!doms || doms.length === 0)return
// update DOM
doms.forEach(dom => {
dom.value = dom.innerHTML = value
})
return Reflect.set(target, key, value, receiver);
}
}
}
该函数返回了一个闭包函数组,封装成函数的形式是为了方便后面注入this的作用域。
主要看set
部分:当有值改变的时候,会通过querySelectorAll
来获取有绑定该被改变模型对象的DOM,当然这部分可以加入Cache缓存绑定结果,就不需要每次都去query一次了,为了思路更清晰,这里先不加。
初始化工作
然后我们定义一个类似Vue配置形式的函数,叫它Sue吧:
1 | function Sue(options) { |
这里面主要一步就是数据通过Proxy来进行劫持绑定,传入的data对象的数值在get/set
操作的时候都会先经过我们上面设置的BindingProxyHandler里面的方法。
最后调用:1
2
3
4
5
6
7
8
9
10window.onload = function () {
let app = new Sue({
el: '#app',
data: {
message: '123'
}
})
document.querySelector('#click').addEventListener('click', () => app.set('message','Hello World'))
}
测试
总结
到这一步,其实已经完成了,整个流程很清晰:
- 劫持数据
get/set
方法 - 监听输入类组件的输入事件,当触发的时候就把值传递给
this.datas[model]
- 传递成功后,就会自动触发BindingProxyHandler中的
set
事件,然后再查询绑定了该数据模型的DOM,将其的’value/innerHTML’设为新的value
。
当然,代码中其实有很多地方可以优化的,例如缓存,防抖等,为了表达清晰脉络,所以简化一下。