一、Vue的双向数据绑定原理
1.1双向数据绑定的流程
new Vue()
首先执行初始化,对data
执行响应化处理,这个过程发生Observe
中- 同时对模板执行编译,找到其中动态绑定的数据,从
data
中获取并初始化视图,这个过程发生在Compile
中 - 同时定义⼀个更新函数和
Watcher
,将来对应数据变化时Watcher
会调用更新函数 - 由于
data
的某个key
在⼀个视图中可能出现多次,所以每个key
都需要⼀个管家Dep
来管理多个Watcher
- 将来data中数据⼀旦发生变化,会首先找到对应的
Dep
,通知所有Watcher
执行更新函数
流程图如下:

1.2 双向数据组成
我们都知道 Vue
是数据双向绑定的框架,双向绑定由三个重要部分构成
-
数据层(Model):应用的数据及业务逻辑,对应data及methods
-
视图层(View):应用的展示效果,各类UI组件
-
业务逻辑层(ViewModel):框架封装的核心,它负责将数据与视图关联起来,就是
数据变化后更新视图, 视图变化后更新数据
二、Vue2 双向数据绑定流程及实现
2.1 双向数据绑定原理
Vue2的双向数据绑定主要是通过Object.defineProperty()这个方法来实现的,就好像有一个小管家在帮你看着数据和页面。
- 数据监听:Object.defineProperty()能给对象的属性添加 getter(获取值的方法)和 setter(设置值的方法)。Vue2会遍历数据对象的所有属性,用这个方法把它们都变成"特殊属性"。比如有个数据对象data,里面有个属性message,Vue2就会给message加上getter和setter。当你读取message的值时,就会触发getter;当你给message赋值时,就会触发setter。这就好像小管家在你访问或修改数据的时候都能知道。
- 发布订阅模式:在Vue2里,还有一个叫做"观察者"的东西。数据变化时,setter会通知这些"观察者","观察者"再去通知依赖这个数据的视图进行更新。比如message的值变了,setter就告诉"观察者","观察者"再告诉页面上显示message的地方,让它更新显示的内容。同样,当用户在页面上修改了数据,比如在输入框里改了message的值,这个变化也会通过"观察者"传给数据对象,实现双向数据绑定。
- 虚拟DOM:Vue2会把真实的DOM结构抽象成一个虚拟DOM树,当数据变化时,不会马上更新真实DOM,而是先在虚拟DOM上进行计算和比较,找出哪些地方需要更新,最后再一次性更新真实DOM。这样可以减少对真实DOM的操作,提高更新效率。
2.2 Object.defineProperty()方法简介
javascript
//直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象
Object.defineProperty(obj,prop,descriptor)
//参数说明:obj:需要定义属性的对象,prop:需被定义或修改的属性名,descriptor:需被定义或修改的属性的描述符。
属性 | 默认值 | 说明 |
---|---|---|
configurable | false | 描述属性是否可以被删除,默认为 false |
enumerable | false | 描述属性是否可以被for...in或Object.keys枚举,默认为 false |
writable | false | 描述属性是否可以修改,默认为 false |
get | undefined | 当访问属性时触发该方法,默认为undefined |
set | undefined | 当属性被修改时触发该方法,默认为undefined |
value | undefined | 属性值,默认为undefined |
2.3 用js实现一个最简单的双向数据绑定
xml
<!DOCTYPE html>
<html>
<head>
<title>简化版双向绑定</title>
<style>
input {width: 100%;padding: 8px;margin-bottom: 10px;box-sizing: border-box;}
.display {margin-top: 20px;padding: 10px;background: #f5f5f5;border-radius: 5px;}
</style>
</head>
<body>
<div class="container">
<input type="text" id="inputField" placeholder="输入内容...">
<div class="display" id="displayText"></div>
</div>
<script>
// 1. 定义一个简单的数据对象
const data = {
message: ''
};
// 2. 使数据对象响应式
Object.defineProperty(data, 'message', {
get() {
return this._message; //之所以不用this.message,是为了避免再次出发getter,发生无限循环,下划线前缀是一种常见的约定,表示这是一个内部属性,不应该直接从外部访问
},
set(newValue) {
this._message = newValue;
// 3. 数据变化时更新DOM
document.getElementById('displayText').textContent = newValue;
// document.getElementById('inputField').value = newValue;
}
});
// 4. 监听输入框变化,更新数据
document.getElementById('inputField').addEventListener('input', function(e) {
data.message = e.target.value;
});
// 5. 初始化显示
data.message = '试试输入内容...';
</script>
</body>
</html>
三、Vue3 双向数据绑定流程及实现
3.1 双向数据绑定原理
Vue3的双向数据绑定原理和Vue2有些不同, Vue3 使用 JavaScript 的 Proxy 对象来实现响应式数据。Proxy 可以监听对象的所有操作,包括读取、写入、删除属性等,从而实现更加灵活和高效的响应式数据。
1、Proxy代理:Proxy可以用来创建一个对象的代理,对这个对象的所有操作进行拦截和处理。Vue3用Proxy来代理数据对象,它比Object.defineProperty()更强大,能直接监听整个对象的变化,而不是像Vue2那样要一个属性一个属性地监听。比如还是有个data对象和message属性,用Proxy代理data后,不管是访问还是修改data里的任何属性,Proxy都能知道,不需要像Vue2那样单独给每个属性设置getter和setter,就好像这个小管家能一下子管好多事情,不用一件一件去处理。
2、响应式系统:Vue3基于Proxy建立了一套响应式系统。当数据变化时,Proxy会拦截到变化,然后通知相关的组件进行更新。这个过程更加高效和灵活,能更好地处理复杂的数据结构和变化。
3、虚拟DOM:Vue3的虚拟DOM也有一些改进,它采用了更高效的算法来比较和更新虚拟DOM,比如静态提升、PatchFlag等技术,能更精准地找到需要更新的地方,进一步提高了更新效率。
3.2响应式系统
3.2.1 reactive函数
Vue3 提供了一个 reactive 函数来创建响应式对象,通过 reactive 函数包装的对象会变成响应式数据,Vue 会自动跟踪这些数据的变化。
javascript
import { reactive } from 'vue';
const state = reactive({
message: 'Hello Vue3'
});
3.2.2 ref 函数
对于基本数据类型,如字符串、数字等,Vue3 提供了 ref 函数来创建响应式数据,使用 ref 包装的值可以在模板中进行双向绑定
csharp
import { ref } from 'vue';
const count = ref(0);
3.2.3 如何双向数据绑定
Vue3 中的双向绑定主要通过 v-model 指令来实现,适用于表单元素,如输入框、复选框等。以下是一个简单的示例:
xml
<template>
<input v-model="message" />
<p>{{ message }}</p>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const message = ref('');
return {
message
}
}
}
</script>