当前位置: 首页 > news >正文

【Vue2和Vue3的双向绑定区别】

Vue2和Vue3的双向绑定区别

  • vue2 双向绑定原理
  • vue3 双向绑定原理
  • Vue2和Vue3的双向绑定存在以下区别:

vue2 双向绑定原理

Vue2 双向绑定的实现主要依赖于 Object.defineProperty() 方法和观察者模式,其中 Object.defineProperty() 方法用于定义属性的 getter 和 setter 方法,观察者模式用于监听数据变化并更新视图。

具体实现步骤如下:

  1. 首先,Vue 将 data 对象中的每个属性都转换为 getter 和 setter 方法,以便在属性值发生变化时能够触发视图的更新,这里使用了 Object.defineProperty() 方法。
function defineReactive(data, key, val) {Object.defineProperty(data, key, {enumerable: true,configurable: true,get: function() {// ...},set: function(newVal) {// ...}});
}
  1. 在 getter 方法中,Vue 将当前的观察者对象添加到该属性的订阅器中,以便在属性值发生变化时能够得到通知并触发视图的更新,这里使用了 Dep.target 属性和观察者模式。
function defineReactive(data, key, val) {const dep = new Dep();Object.defineProperty(data, key, {// ...get: function() {if (Dep.target) {dep.addSub(Dep.target);}// ...},// ...});
}
  1. 在 setter 方法中,Vue 首先更新属性的值,然后遍历该属性的订阅器,并调用每个观察者对象的 update() 方法,以便通知它们属性发生了变化,这里同样使用了观察者模式。
function defineReactive(data, key, val) {const dep = new Dep();Object.defineProperty(data, key, {// ...set: function(newVal) {if (val === newVal) {return;}val = newVal;dep.notify();}});
}
  1. 在组件初始化时,Vue 实例化一个 Watcher 对象,该对象会调用 data 中的属性 getter 方法,并将自身添加到该属性的订阅器中,以便在属性值发生变化时能够得到通知并触发视图的更新,这里使用了观察者模式。
function Watcher(vm, expOrFn, cb) {this.vm = vm;this.cb = cb;this.getter = parsePath(expOrFn);this.value = this.get();
}Watcher.prototype.get = function() {Dep.target = this;const value = this.getter.call(this.vm, this.vm);Dep.target = null;return value;
};
  1. 当某个属性的值发生变化时,该属性的订阅器会遍历其中的所有观察者对象,并调用它们的 update() 方法,以便通知它们属性发生了变化,这里同样使用了观察者模式。
function Dep() {this.subs = [];
}Dep.prototype.addSub = function(sub) {this.subs.push(sub);
};Dep.prototype.notify = function() {this.subs.forEach(function(sub) {sub.update();});
};

这样,当数据发生变化时,观察者模式会实时地通知所有依赖该数据的组件,在组件中更新相应的视图。

以上是 Vue2 双向绑定的大致实现原理,具体可以参考 Vue 源码。

vue3 双向绑定原理

Vue3 的双向绑定原理与 Vue2 类似,都是基于 Object.defineProperty 实现的。不过,Vue3 对此做了一些改进,通过 Proxy 实现了更高效的双向绑定。

Proxy 的基本使用方法是通过将对象包装在一个句柄中来拦截对该对象的访问。当访问对象时,句柄会调用相关的拦截方法。

下面我们通过一个简单的示例来了解 Vue3 双向绑定的实现原理。

首先,我们初始化一个 Vue3 实例:

const app = Vue.createApp({data() {return {count: 0}}
})const vm = app.mount("#app")

然后,我们为 count 属性添加一个双向绑定:

<input type="text" v-model="count">

此时,我们需要在数据对象上添加一个 getter 和一个 setter 方法,使得在修改输入框的值时,数据对象也会同步更新。这可以通过 Proxy 来实现。

我们可以在 Vue3 组件的 setup 函数中使用 reactive 函数来创建响应式数据对象。reactive 函数采用的就是 Proxy 来实现数据的双向绑定。

const { reactive } = Vueconst state = reactive({ count: 0 })watch(() => {console.log(state.count)
})

当修改数据对象中的 count 属性时,会触发 watch 中的监听函数,输出新的 count 值。

在原理上,Vue3 会为数据对象中的每个属性创建一个 Proxy 对象,并通过该对象的 get 和 set 方法来实现数据对象的双向绑定。

下面是 Vue3 的源码分析:

  1. reactive 函数
function reactive(obj) {if (!isObject(obj)) {return obj}// 对象已经被代理过了,直接返回它的代理对象if (obj.__v_proxy) {return obj.__v_proxy}// 创建 Proxy 对象const observed = new Proxy(obj, baseHandlers)// 缓存代理对象并返回obj.__v_proxy = observedreturn observed
}

在 reactive 函数中,我们首先对传入的 obj 进行判断,如果不是对象或者已经被代理过了,直接返回该对象。

如果 obj 尚未被代理,则使用 Proxy 对象创建一个新的代理对象 observed,并缓存该代理对象到原始对象 obj 的 __v_proxy 属性中,并返回 observed。

  1. baseHandlers

创建 Proxy 对象的关键在于使用 Proxy 的句柄(handler)。该句柄对象包含了一系列的拦截方法,例如 get 和 set 方法,用于拦截对对象属性的访问和修改。

Vue3 中的 baseHandlers 是在 createReactiveObject 函数中定义的。它是一个包含了处理属性的 getter 和 setter 的对象。其中,getter 方法会返回原始值,setter 方法则会通过 emit 调用来触发更新。

const mutableHandlers = {get: createGetter(),set: createSetter()
}function createGetter() {return function get(target, key, receiver) {const res = Reflect.get(target, key, receiver)return isObject(res) ? reactive(res) : res}
}function createSetter() {return function set(target, key, value, receiver) {const oldValue = target[key]const result = Reflect.set(target, key, value, receiver)if (result && oldValue !== value) {trigger(target, key)}return result}
}

在上述代码中,我们使用了 Reflect 的 get 和 set 方法来代替直接操作原始对象(obj)的方式。这么做是因为使用 Reflect 方法可以处理更多的情况,并且保证了代码的健壮性。

简要解释一下这两个拦截器函数,createGetter 方法用于拦截对象属性的读取操作。当我们读取对象属性时,如果该属性是对象,则递归调用 reactive 函数来创建对该对象的代理。

createSetter 方法用于拦截对象属性的赋值操作。当我们为对象的属性赋值时,会触发该拦截器的 setter 方法。我们需要在该方法中判断新的值是否与旧值相同,如果不同,则调用 trigger 函数以触发更新。

  1. trigger 函数

trigger 函数的作用是触发数据更新,通知视图进行重新渲染。

const effectStack = []function trigger(target, key) {const depsMap = targetMap.get(target)if (!depsMap) {return}const effects = new Set()const add = (effectsToAdd) => {effectsToAdd.forEach(effect => {effects.add(effect)})}const run = (effect) => {if (effect.options.scheduler) {effect.options.scheduler(effect)} else {effect()}}if (key) {const dep = depsMap.get(key)if (dep) {add(dep)}} else {targetMap.forEach((dep, key) => {if (key === 'length' || isArray(target) && parseInt(key, 10) >= target.length) {add(dep)}})}effects.forEach(run)
}

在 trigger 函数中,我们首先获取与目标对象关联的依赖表 depsMap。然后,我们遍历依赖表,根据依赖项的数量触发数据更新操作。

  1. effect 函数

effect 函数可以用于创建一个响应式的副作用,当关联的数据发生变化时,会自动更新视图。

function effect(fn, options = {}) {const effect = createReactiveEffect(fn, options)if (!options.lazy) {effect()}return effect
}function createReactiveEffect(fn, options) {const effect = function reactiveEffect() {try {// 入栈effectStack.push(effect)return fn()} finally {// 出栈effectStack.pop()}}effect.id = uid++effect._isEffect = trueeffect.raw = fneffect.deps = []effect.options = optionsreturn effect
}

在 effect 函数中,我们首先使用 createReactiveEffect 函数创建一个新的响应式副作用 effect,并将其返回。createReactiveEffect 函数主要用于创建响应式副作用的内部实现,包括将副作用函数 fn 转换为响应式版本、保存响应式副作用与其相关的状态等。

在响应式副作用创建完成后,我们可以直接调用该副作用(即执行 effect 函数),也可以将其作为参数传递给其他地方使用。

总结

Vue3 双向绑定的原理与 Vue2 并没有本质区别,都是使用 Object.defineProperty 或者 Proxy 实现的双向绑定。不同之处在于,Vue3 采用了更高效的 Proxy 实现方式,并且对一些细节做了优化,提高了整个框架的性能。

此外,在 Vue3 中,由于使用了拦截器函数来对数据进行包装,因此其内部实现也更加复杂。不过,理解 Vue3 双向绑定的原理对于我们深入理解整个框架的设计思想和实现方式非常有帮助。

Vue2和Vue3的双向绑定存在以下区别:

1. 响应式系统的改进:Vue3通过Proxy替换了Vue2中使用的Object.defineProperty来实现响应式数据。这使得Vue3的响应式系统更加高效和灵活,可以更好地支持嵌套对象和数组。

2. 组件的更新策略:Vue2中的组件更新是通过递归式处理的,即每次更新时会遍历整个组件树,这样会导致效率较低。Vue3中采用了静态分析技术进行组件更新,可以更好地实现局部更新,提高渲染效率。

3. 模板语言的改进:Vue3中提供了更加灵活的模板语法,并增加了一些新特性,例如:v-model的多个绑定值、v-model修饰符的增加、el和ref的区别等。

4. 生命周期的改变:Vue3中的生命周期函数名称发生了改变。例如:created改为了setup,beforeDestroy改为了unmounted。

5. Composition API:Vue3中引入了Composition API,可以使得组件的逻辑更加清晰和组织化。它通过将相关的逻辑组合成一个逻辑组合体提高代码的可维护性和可读性。

总的来说,Vue3在双向绑定方面做了很多的改进和优化,可以更好地满足现代应用程序的需求。

相关文章:

【Vue2和Vue3的双向绑定区别】

Vue2和Vue3的双向绑定区别 vue2 双向绑定原理vue3 双向绑定原理Vue2和Vue3的双向绑定存在以下区别&#xff1a; vue2 双向绑定原理 Vue2 双向绑定的实现主要依赖于 Object.defineProperty() 方法和观察者模式&#xff0c;其中 Object.defineProperty() 方法用于定义属性的 get…...

【再识C进阶3(下)】详细地认识字符分类函数,字符转换函数和内存函数

前言 &#x1f493;作者简介&#xff1a; 加油&#xff0c;旭杏&#xff0c;目前大二&#xff0c;正在学习C&#xff0c;数据结构等&#x1f440; &#x1f493;作者主页&#xff1a;加油&#xff0c;旭杏的主页&#x1f440; ⏩本文收录在&#xff1a;再识C进阶的专栏&#x1…...

windows WSL配置cuda,pytorch和jupyter notebook

机器配置 GPU: NVIDIA Quadro K2000 与 NVIDIA 驱动程序捆绑的CUDA版本 但按照维基百科的描述&#xff0c;我的GPU对应的compute capability3.0&#xff0c;允许安装的CUDA最高只支持10.2&#xff0c;如下所示。 为什么本地会显示11.4呢&#xff1f;对此&#xff0c;GPT是这…...

回调地狱的产生=>Promise链式调用解决

常见的异步任务包括网络请求、文件读写、定时器等。当多个异步任务之间存在依赖关系&#xff0c;需要按照一定的顺序执行时&#xff0c;就容易出现回调地狱的情况。例如&#xff0c;当一个网络请求的结果返回后&#xff0c;需要根据返回的数据进行下一步的操作&#xff0c;这时…...

【设计模式】六、建造者模式

文章目录 需求介绍角色应用实例建造者模式在 JDK 的应用和源码分析java.lang.StringBuilder 中的建造者模式 建造者模式的注意事项和细节 需求 需要建房子&#xff1a;这一过程为打桩、砌墙、封顶房子有各种各样的&#xff0c;比如普通房&#xff0c;高楼&#xff0c;别墅&…...

SpringBoot 可以同时处理多少请求

一、前言 首先&#xff0c;在Spring Boot应用中&#xff0c;我们可以使用 Tomcat、Jetty、Undertow 等嵌入式 Web 服务器作为应用程序的运行容器。这些服务器都支持并发请求处理的能力。另外&#xff0c;Spring Boot 还提供了一些配置参数&#xff0c;可以对 Web 服务器进行调…...

嵌入式Linux应用开发-驱动大全-第一章同步与互斥②

嵌入式Linux应用开发-驱动大全-第一章同步与互斥② 第一章 同步与互斥②1.3 原子操作的实现原理与使用1.3.1 原子变量的内核操作函数1.3.2 原子变量的内核实现1.3.2.1 ATOMIC_OP在 UP系统中的实现1.3.2.2 ATOMIC_OP在 SMP系统中的实现 1.3.3 原子变量使用案例1.3.4 原子位介绍1…...

EasyExcel的源码流程(导入Excel)

1. 入口 2. EasyExcel类继承了EasyExcelFactory类&#xff0c;EasyExcel自动拥有EasyExcelFactory父类的所有方法&#xff0c;如read()&#xff0c;readSheet()&#xff0c;write()&#xff0c;writerSheet()等等。 3. 进入.read()方法&#xff0c;需要传入三个参数(文件路径…...

基于 jasypt 实现spring boot 配置文件脱敏

前言 在项目构建过程中&#xff0c;保护敏感信息的安全性至关重要&#xff0c;为了提高系统的安全性能&#xff0c;我们采用了Jasypt来对配置文件中的敏感信息进行加密处理&#xff0c;以确保系统的机密信息不被轻易泄露。 步骤 添加Maven依赖 首先&#xff0c;我们需要添加…...

Python——ASCII编码与Unicode(UTF-8,UTF-16 和 UTF-32)编码

Python3 Python——ASCII编码与Unicode&#xff08;UTF-8&#xff0c;UTF-16 和 UTF-32&#xff09;编码 文章目录 Python3一、编码与编码格式二、ASCII编码与UTF-8编码&#xff08;UTF-16 和 UTF-32编码&#xff09;三、ASCII 字符串和 Unicode 字符串 最近看Python程序的文件…...

【多媒体技术与实践】音频信息获取和处理——编程题汇总

1&#xff1a;音频信息数据量计算 已知采样频率&#xff08;单位KHz&#xff09;、量化位数、声道数及持续时间&#xff08;单位分钟&#xff09;&#xff0c;求未压缩时的数据量&#xff08;单位MB&#xff09;. 例如&#xff1a; 输入&#xff1a; 22.05 16 2 3 &#xff…...

堆优化迪氏最短单源路径原理及C++实现

时间复杂度 O(ElogE)&#xff0c;E是边数。适用与稀疏图。 使用前提 边的权为正。可以非连通&#xff0c;非连通的距离为-1。 原理 优选队列&#xff08;小根堆&#xff09;记录两个数据&#xff1a;当前点到源点距离&#xff0c;当前点。先处理距离小的点&#xff1b;如果…...

Leetcode202. 快乐数

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0…...

【MySQL】MySql常见面试题总结

目录 一、什么是sql注入 二、sql语句的执行流程 三、内连接和外连接的区别 四、Union和Union All 有什么区别 五、MySql如何取差集 六、DELETE和TRUNCATE有什么区别 七、count&#xff08;*&#xff09;和count&#xff08;1&#xff09;的区别 八、MyISAM和InnoDB的区…...

【Java 进阶篇】JDBC PreparedStatement 详解

在Java中&#xff0c;与关系型数据库进行交互是非常常见的任务之一。JDBC&#xff08;Java Database Connectivity&#xff09;是Java平台的一个标准API&#xff0c;用于连接和操作各种关系型数据库。其中&#xff0c;PreparedStatement 是 JDBC 中一个重要的接口&#xff0c;用…...

嵌入式Linux应用开发-驱动大全-第一章同步与互斥①

嵌入式Linux应用开发-驱动大全-第一章同步与互斥① 第一章 同步与互斥①1.1 内联汇编1.1.1 C语言实现加法1.1.2 使用汇编函数实现加法1.1.3 内联汇编语法1.1.4 编写内联汇编实现加法1.1.5 earlyclobber的例子 1.2 同步与互斥的失败例子1.2.1 失败例子11.2.2 失败例子21.2.3 失败…...

【计算机网络】 基于UDP的简单通讯(客户端)

文章目录 客户端流程代码实现添加头文件以及库依赖加载库创建套接字发送接收数据关闭套接字、卸载库 测试 客户端 流程 客户端跟服务端差不多&#xff0c;也要先加载库&#xff0c;在加载库之后也要创建套接字&#xff0c;但是客户端一定是没有绑定ip地址的&#xff0c;之后是…...

【云备份项目】:环境搭建(g++、json库、bundle库、httplib库)

文章目录 1. g 升级到 7.3 版本2. 安装 jsoncpp 库3. 下载 bundle 数据压缩库4. 下载 httplib 库从 Win 传输文件到 Linux解压缩 1. g 升级到 7.3 版本 &#x1f517;链接跳转 2. 安装 jsoncpp 库 &#x1f517;链接跳转 3. 下载 bundle 数据压缩库 安装 git 工具 sudo yum…...

电脑右键新建记事本不见了--设置恢复篇(无需操作注册表)

电脑右键新建记事本不见了–设置恢复篇&#xff08;无需修改注册表&#xff09; 电脑不知怎么想右键新建记事本结果竟然不见了&#xff0c;搜寻网上的都是什么修改注册表&#xff0c;粘贴代码修复&#xff08;感觉太复杂了&#xff09;&#xff0c;这里介绍通过设置内重新对记…...

JavaScript内置对象 - Array数组(四)- 序列生成器

序列生成器是生成一个指定起始值和结束值的序列&#xff0c;并且根据指定间隔长度&#xff0c;生成序列数组。 完成此功能需要使用到Array内置对象的from()对象&#xff0c;以及类数组相关知识&#xff0c;前面几篇有相关案例进行演示。 地址一&#xff1a;JavaScript内置对象…...

基于OpenClaw构建开源项目与Docker镜像自动化监控方案

1. 项目概述 作为一个常年泡在开源社区和容器生态里的开发者&#xff0c;我深知“追新”的痛。今天这个项目发布了v2.0&#xff0c;明天那个镜像更新了安全补丁&#xff0c;手动去GitHub和Docker Hub一个个检查&#xff0c;效率低不说&#xff0c;还容易遗漏关键更新。为了解决…...

C++智能指针详解:原理、使用及避坑指南

文章目录 前言 一、智能指针核心原理&#xff1a;RAII机制 二、C常用智能指针详解&#xff08;重点掌握后两种&#xff09; 三、智能指针高频坑点&#xff08;重中之重&#xff09; 四、三大智能指针对比&#xff08;选择指南&#xff09; 五、实战案例&#xff1a;智能指…...

全景视频会议核心技术解析:从200°视场角到实时图像拼接

1. 项目概述&#xff1a;全景视频会议如何从概念走向现实视频会议这玩意儿&#xff0c;我们搞通信和消费电子这行的&#xff0c;这些年见得多了。从最早模糊不清的像素块&#xff0c;到后来高清但视角固定的摄像头&#xff0c;大家总觉得少了点什么。没错&#xff0c;少的就是那…...

观测多模型API调用延迟与稳定性选择合适服务商

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 观测多模型API调用延迟与稳定性选择合适服务商 在实际项目开发中&#xff0c;直接依赖单一模型服务商可能会面临服务波动或响应延迟…...

对比直接使用厂商API,Taotoken在路由容灾上的体验差异

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 对比直接使用厂商API&#xff0c;Taotoken在路由容灾上的体验差异 1. 引言&#xff1a;服务稳定性的现实挑战 在将大模型能力集成…...

3步终极指南:用开源TCC-G15彻底解决Dell G15散热难题

3步终极指南&#xff1a;用开源TCC-G15彻底解决Dell G15散热难题 【免费下载链接】tcc-g15 Thermal Control Center for Dell G15 - open source alternative to AWCC 项目地址: https://gitcode.com/gh_mirrors/tc/tcc-g15 你是否正在为Dell G15笔记本的过热问题而烦恼…...

构建毫秒级实时传输系统:基于flv.js的低延迟架构优化方案

构建毫秒级实时传输系统&#xff1a;基于flv.js的低延迟架构优化方案 【免费下载链接】flv.js HTML5 FLV Player 项目地址: https://gitcode.com/gh_mirrors/fl/flv.js flv.js作为HTML5 FLV播放器的核心技术方案&#xff0c;通过Media Source Extensions实现浏览器端FLV…...

【Midjourney Gouache风格终极指南】:20年AI绘画专家亲授7大参数黄金组合与3类易踩翻车点

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Gouache风格的本质解构与AI绘画语境迁移 Gouache&#xff08;水粉画&#xff09;并非简单意义上的“不透明水彩”&#xff0c;其本质在于颜料颗粒的物理遮盖性、媒介乳化稳定性与干湿叠压响应的三重耦合…...

Cyberpunk 2077存档编辑器:终极免费工具完整使用指南

Cyberpunk 2077存档编辑器&#xff1a;终极免费工具完整使用指南 【免费下载链接】CyberpunkSaveEditor A tool to edit Cyberpunk 2077 sav.dat files 项目地址: https://gitcode.com/gh_mirrors/cy/CyberpunkSaveEditor 你是否想要在《赛博朋克2077》中拥有无限可能&a…...

Rust与Godot引擎集成:使用gdext构建高性能游戏模块

1. 项目概述&#xff1a;当Rust遇上Godot 如果你是一名游戏开发者&#xff0c;同时又对Rust语言的安全性、性能和现代特性着迷&#xff0c;那么你很可能和我一样&#xff0c;曾经在两个优秀的工具之间感到难以抉择。一边是上手快、生态繁荣的Godot引擎&#xff0c;另一边是能让…...