Bootstrap

浏览器漫谈HTML--2.2从表单标签看vue的响应式系统 理论+实战

表单标签的双向绑定是一个很有亮点的功能。在不同框架中他实现这个功能大同小异,这里我们介绍几个常见的框架中他是如何实现双向绑定的。

原生的input输入框是没有双向绑定的功能的。取而代之的,它的input上有一个event对象,这个对象中有一个属性target,而这个target的值就是此时输入框内部的数据。所以如果是原生input,我们需要手动将这个input绑定到我们用于接收这个数据的参数上。

<!-- 原生input -->
<input type="text" value="初始值" oninput="viewToData(event)">

<script>
let data = {
  value: "初始值"
};

function dataToView(){
    //数据变化的时候触输入框数据更新
    document.querySelector('input').value = data.value;
}

function viewToData(rvent){
    //输入框数据变化触发底层数据更新
    data.value = event.target.value;
}
</script>

非常的难看吧。现在很多框架已经帮你做了这一步,自动处理了数据和视图的同步,并且还能处理复杂的数据结构和组件通信,让开发更高效

双向绑定

vue2中想要绑定数据 只需要使用v-model指定参数即可。这是一个语法糖 其实就是将

:value="data",@input = "function"

化简成了一句“v-model"而已。

然后的然后  这样监听数据变化的底层原理又是什么呢?

其实是vue底层的响应式系统在做支撑。

响应式系统

特点

响应式系统是react和vue,特别是vue的一大核心特性。响应式系统是一种能够持续响应变化并自动进行适应的系统,在vue和react中,体现在数据的变化会触发视图的更新。

让我们从0开始,看看要如何才能实现”数据的变化触发视图的更新"。

如果是让我们自己设计,我们会怎么做?肯定就是要监听数据变化,当数据变化的时候给视图更新函数打报告对吧。

那么其实就分为两步,监听数据的变化,以及传递数据数据变化的消息给视图,触发更新。

我们先讲第一个。

如何监听数据变化

一般来说,想要实现监听数据的变化,有这几大方法(设计思路):

数据绑定

        数据绑定的核心思路是将数据和视图层进行连接,使得数据变化时视图自动更新,视图变化时数字更新。在vue中,主要用v-bind实现单项绑定(数据->视图)和v-model(数据<=>视图)。

观察者模式

        观察者模式是一种一对多的关系,一个被观察对象多个观察者。当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并且自动更新。这种方式能使得对象中的耦合度降低。

发布-订阅者模式

        发布-订阅者模式可以说是一对一关系,允许发布者发送消息,订阅者接收。发布者和订阅者之间没有直接直接通信 而是通过一个中央事件总线或消息代理进行沟通,发布者将事件发送到事件总线,订阅者从事件总线接收消息。有点像js的事件处理队列。

事件驱动模式

        事件驱动模式通过事件的发布和处理来驱动程序的执行,组件通过监听特定的事件来响应用户操作或系统状态变化。使用事件监听器和数据触发机制,组件可以注册事件处理函数。

        数据劫持

        通过调用defineProperty(或proxy),定义setter和getter方法,在数据被读取或修改时执行特定的逻辑。在响应式系统中,数据劫持会在setter时进行以来手机,也就是记录当前的观察者-正在渲染的组件。这通常通过一个全局的依赖数组实现,数据中存储了所有依赖于该数据的组件。在setter执行时,会通知所有依赖于该数据的组件进行更新。

在响应式设计中,实现监听数据变化主要依赖于观察者模式和数据劫持。

数据劫持专注于数据的拦截和响应,确保数据变化时能自动更新视图;而观察者模式则关注对象之间关系和通知机制,适用于更复杂的交互场景。换而言之,数据劫持保证了基础的响应式能力,而观察者模式则允许多个组件或对象之间的解耦和交互。通过这两个手段,实现了响应式设计。

如何通知对应组件进行更新

上文在数据劫持中提到过,依赖收集会在getter中通知所有需要更新的组件。看似很完美的背后其实也是存在问题的:这样的更新,消耗必然不小。那么如何保持尽量高效的速度实现视图的更新呢?

基本分成下面几步:

标记更新

        当数据被更新是,响应式系统会遍历其依赖列表,标记所有依赖于该数据的组件为"需要个更新".

异步更新

        为了提高性能,响应式系统会将需要更新的组件放进一个队列中,使用异步的方式进行更新处理。

        是不是很眼熟?没错,正是js的事件循环机制。事件循环机制允许视图更新事操作放进微任务队列中,顺着队列的执行顺序进行更新。

虚拟dom

        在更新视图时,框架首先会生成新的虚拟DOM树,接着和旧的虚拟DOM树进行比较(diff)。通过比较,框架能找出哪些部分发生了变化,完成最小的更新操作。通过将多个操作合并为一次DOM操作,框架能显著提升性能,减少重排和重绘次数。

实战

实现一个简单的双向绑定

<template>
  <title>This is title</title>
  <input type="text" id="id-input" @input="handleInput"/><br />
  <div id="id-div">{{ inputValue }}</div>
</template>

<script setup>
import { ref,watch } from 'vue';

const inputValue = ref('');

// 监听 input 事件
const handleInput = (event) => {
  console.log(event.target.value);
  //input被触发时更改inputValue
  inputValue.value = event.target.value;
};

//inputValue被更改时更改输入框表单的值
watch(inputValue, (newValue) => {
  document.getElementById('id-input').value = newValue;
});

</script>

;