持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
前言
学习框架,仅仅会使用是不够的,想要更熟悉框架,需要对源码有一定的了解。本篇文章通过vue源码解析,实现vue的双向绑定。
介绍
单向绑定,是将Model绑定到View中,如果更新了Model,那么View中的数据也会跟着改变,此时就是单向绑定
双向绑定,在单向绑定的基础上,如果修改View中的数据,Model中数据也会跟着改变,此时就是双向绑定
<div id="app">
<h1>{{message}}</h1>
<input type="text" v-model="message">
</div>
<script src="vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
message: 'helloWorld',
}
})
</script>
实现
接下来,我们就来介绍一下vue中双向绑定v-model的原理,并实现
-
创建双向绑定的
DOM结构<input v-model="message"> -
在
vue中,循环每一个节点,找到包含v-model的节点通过
hasAttribute('v-model')进行判断节点是否添加了v-model-
如果节点添加了
v-model,就获取属性值通过
getAttribute('v-model')获取属性名为v-model的属性值 -
还需要判断
data中是否有v-model对应属性值通过
hasOwnProperty(vmKey)判断data中是否有vmKey属性,这里的vmKey是messageif(this.hasOwnProperty(vmKey)){ console.log(this,this[vmKey]) }此时的
this是vue对象,通过vue[vmKey]可以获取到属性对应的值问题:
获取不到
data中的message,只能获取到data中$data下的messageconsole.log(this,vmKey,this[vmKey],this.$data[vmKey])所以我就通过判断
data中的$data是否有vmKey属性if(this.$data.hasOwnProperty(vmKey)){ console.log(this.$data[vmKey]) }-
如果有对应属性值,那么就把
data中对应的值取出来,赋值给当前节点的value中例如:
v-model绑定了message,那么还需要判断data中是否有message属性,如果有,就取出data中message对应的属性值,将对应属性值赋值给,v-model绑定的input框的value,从而实现Model数据传输到View中还需要实现
v-model绑定的input框中value值改变时,data中对应的属性也要跟着改变,这里需要利用前面说的数据劫持if(this.$data.hasOwnProperty(vmKey)){ // data中对应的值取出来,赋值给当前节点的value中 item.value = this.$data[vmKey] } -
为节点添加事件
为节点添加事件,将
input中的value赋值给data中的message,此时就能实现双向绑定了item.addEventListener('input',e => { this[vmKey] = item.value })这里的前提是对数据进行劫持了,当我们把value值赋值给对象的属性中,observe()中的set就会监听到,进而调用update去更新视图
-
-
完整代码
// 双向绑定,判断元素节点是否被包含v-model
if(item.hasAttribute('v-model')){
// 获取属性名为v-model对应的属性值,去除两边空格
let vmKey = item.getAttribute('v-model').trim()
// 判断当前节点中是否包含属性值(例:message),即data中是否有message属性,这里的this是vue对象
// 问题:获取不到data中的message,只能获取到data中$data下的message ,这里不太理解
// console.log(this,vmKey,this[vmKey],this.$data[vmKey])
if(this.$data.hasOwnProperty(vmKey)){
// data中对应的值取出来,赋值给当前节点的value中
item.value = this.$data[vmKey]
}
// 为节点添加事件,将input中的value赋值给data中的message,此时就能实现双向绑定了
item.addEventListener('input',e => {
this.$data[vmKey] = item.value
})
}
效果
总结
双向绑定是改变Model然后View也会改变,改变View然后Model也会改变,
Model->View 实际上是通过Object.defineProperty劫持数据的改变,如果数据改变了,就更新视图(数据变化,set监听到后调用update去更新视图)
View->Model 则是通过监听元素节点中包含 v-model 的节点,并添加事件,监听value变化。例如是input绑定了v-model,首先找到该input,然后就为input绑定事件,监听input中value的变化,只要value变化,就将值赋给vue对象中的data,此时data变化又触发Model->View