前言
Vue 框架是一种 MVVM 框架,它有一个很大的特点就是数据双向绑定,在开发过程中我们只需要操作 Model ,而不需要修改 View ,使用起来 VR 因吹斯汀。但是它的实现原理并不复杂,主要是运用了设计模式中的观察者模式,也可以说是加了钩子函数。下面用原生 JS 实现一下。
代码实现
创建模板
创建一个 html 模板,包含一个 <input> 和一个 <span> 标签,我们要实现的目标就是让 v-model 中的 message 与 v-bind 中的 message 数据绑定。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="myapp">
<input v-model="message"/><br>
<span v-bind="message"></span>
</div>
</body>
</html>
创建对象
首先,我们要先创建一个 model 对象,并添加属性 message,然后通过 querySelectorAll 获取包含 v-model 属性且值为 “message” 的所有对象,保存在数组 models 中,然后遍历数组获取用户的输入,并赋值给定义的 model。
var model = {
message : ""
};
var models = myapp.querySelectorAll("[v-model=message]");
for (var i = 0; i < models.length; i++) {
models[i].onkeyup = function () {
model[this.getAttribute("v-model")] = this.value;
}
}
实现原理
接下来,通过 defineProperty 来定义model 对象属性,这里主要添加 set 和 get 方法,这两个方法是实现双向绑定的关键。定义这两个方法后,当我们为 model 赋值时,会自动调用 set 方法;当获取 model 的值时,会自动调用 get 方法。因此,我们可以通过 set 方法,当检测到用户输入时,在给 model 赋值之前,先给绑定了此数据的 v-bind 数据赋值,再给 v-model 对象赋值,最终给 model 对象赋值。代码如下:
// 观察者模式 / 钩子函数
// defineProperty 来定义一个对象的某个属性
Object.defineProperty(model,"message",{
set:function (newValue) {
var binds = myapp.querySelectorAll("[v-bind=message]");
for (var i = 0; i < binds.length; i++) {
binds[i].innerHTML = newValue;
};
var models = myapp.querySelectorAll("[v-model=message]");
for (var i = 0; i < models.length; i++) {
models[i].value = newValue;
};
this.value = newValue;
},
get:function () {
return this.value;
}
})
如此,就完成了最简单的数据双向绑定。但是在这样的数据绑定还是有很多问题,Vue 中的源码实现也要更加复杂,下一篇博客会对数据绑定做一下封装,让他的实用性变得更强。
全部代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="myapp">
<input v-model="message"/><br>
<span v-bind="message"></span>
</div>
<!--JavaScript-->
<!--<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>-->
<script type="text/javascript">
var model = {
message : ""
};
var models = myapp.querySelectorAll("[v-model=message]");
for (var i = 0; i < models.length; i++) {
models[i].onkeyup = function () {
model[this.getAttribute("v-model")] = this.value;
}
}
// 观察者模式 / 钩子函数
// defineProperty 来定义一个对象的某个属性
Object.defineProperty(model,"message",{
set:function (newValue) {
var binds = myapp.querySelectorAll("[v-bind=message]");
for (var i = 0; i < binds.length; i++) {
binds[i].innerHTML = newValue;
};
var models = myapp.querySelectorAll("[v-model=message]");
for (var i = 0; i < models.length; i++) {
models[i].value = newValue;
};
this.value = newValue;
},
get:function () {
return this.value;
}
})
</script>
</body>
</html>