Vue双向绑定原理实现——观察者模式

前言

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>


猜你喜欢

转载自blog.csdn.net/TalonZhang/article/details/82860984