搜索
首页web前端Vue.jsVue3中的响应式机制是什么

Vue3中的响应式机制是什么

May 12, 2023 pm 05:07 PM
vue3

什么是响应式

响应式一直都是 Vue 的特色功能之一;与之相比,JavaScript 里面的变量,是没有响应式这个概念的;你在学习 JavaScript 的时候首先被灌输的概念,就是代码是自上而下执行的;

我们看下面的代码,代码在执行后,打印输出的两次 double 的结果也都是 2;即使 我们修改了代码中 count 的值后,double 的值也不会有任何改变

let count = 1
let double = count * 2
count = 2

double 的值是根据 count 的值乘以二计算而得到的,如果现在我们想让 doube 能够跟着 count 的变化而变化,那么我们就需要在每次 count 的值修改后,重新计算 double

比如,在下面的代码,我们先把计算 doube 的逻辑封装成函数,然后在修改完 count 之 后,再执行一遍,你就会得到最新的 double 值

let count = 1
// 计算过程封装成函数
let getDouble = n=>n*2 //箭头函数
let double = getDouble(count)
count = 2
// 重新计算double ,这里我们不能自动执行对double的计算
double = getDouble(count)

实际开发中的计算逻辑会比计算 doube 复杂的多,但是都可以封装成一个函数去执行;下 一步,我们要考虑的是,如何让 double 的值得到自动计算

如果我们能让 getDouble 函数自动执行,也就是如下图所示,我们使用 JavaScript 的某种机制,把 count 包裹一层,每当对 count 进行修改时,就去同步更新 double 的值,那 么就有一种 double 自动跟着 count 的变化而变化的感觉,这就算是响应式的雏形了

Vue3中的响应式机制是什么


响应式原理

响应式原理是什么呢?Vue 中用过三种响应式解决方案,分别是 defineProperty、Proxy 和 value setter我们首先来看 Vue 2 的 defineProperty API

这里我结合一个例子来说明,在下面的代码中,我们定义个一个对象 obj,使用 defineProperty 代理了 count 属性;这样我们就对 obj 对象的 value 属性实现了拦截,读取 count 属性的时候执行 get 函数,修改 count 属性的时候执行 set 函数,并在 set  函数内部重新计算了 double

let getDouble = n=>n*2
let obj = {}
let count = 1
let double = getDouble(count)
Object.defineProperty(obj,'count',{
    get(){
        return count
    },
    set(val){
        count = val
        double = getDouble(val)
    }
})
console.log(double) // 打印2
obj.count = 2
console.log(double) // 打印4 有种自动变化的感觉

这样我们就实现了简易的响应式功能,在课程的第四部分,我还会带着你写一个更完善的响应式系统

但 defineProperty API 作为 Vue 2 实现响应式的原理,它的语法中也有一些缺陷;比如在下面代码中,我们删除 obj.count 属性,set 函数就不会执行,double 还是之前的数值;这也是为什么在 Vue 2 中,我们需要 $delete 一个专门的函数去删除数据

delete obj.count
console.log(double) // doube还是4

Vue 3 的响应式机制是基于 Proxy 实现的;就 Proxy 这个名字来说,你也能看出来这是代理的意思,Proxy 的重要意义在于它解决了 Vue 2 响应式的缺陷

我们看下面的代码,在其中我们通过 new Proxy 代理了 obj 这个对象,然后通过 get、set 和 deleteProperty 函数代理了对象的读取、修改和删除操作,从而实现了响应式的功能

let proxy = new Proxy(obj,{
    get : function (target,prop) {
        return target[prop]
    },
    set : function (target,prop,value) {
        target[prop] = value;
        if(prop==='count'){
            double = getDouble(value)
    }
},
deleteProperty(target,prop){
    delete target[prop]
    if(prop==='count'){
        double = NaN
    }
   }
})
console.log(obj.count,double)
proxy.count = 2
console.log(obj.count,double)
delete proxy.count
// 删除属性后,我们打印log时,输出的结果就会是 undefined NaN
console.log(obj.count,double)

我们从这里可以看出 Proxy 实现的功能和 Vue 2 的 definePropery 类似,它们都能够在用户修改数据的时候触发 set 函数,从而实现自动更新 double 的功能。而且 Proxy 还完善了几个 definePropery 的缺陷,比如说可以监听到属性的删除

Proxy 是针对对象来监听,而不是针对某个具体属性,所以不仅可以代理那些定义时不存在的属性,还可以代理更丰富的数据结构,比如 Map、Set 等,并且我们也能通过 deleteProperty 实现对删除操作的代理

当然,为了帮助你理解 Proxy,我们还可以把 double 相关的代码都写在 set 和 deleteProperty 函数里进行实现,在课程的后半程我会带你做好更完善的封装;比如下面代码中,Vue 3 的 reactive 函数可以把一个对象变成响应式数据,而 reactive 就是基于 Proxy 实现的;我们还可以通过 watchEffect,在 obj.count 修改之后,执行数据的打印

import {reactive,computed,watchEffect} from 'vue'
let obj = reactive({
    count:1
})
let double = computed(()=>obj.count*2)
obj.count = 2
watchEffect(()=>{
    console.log('数据被修改了',obj.count,double.value)
})

有了 Proxy 后,响应式机制就比较完备了;但是在 Vue 3 中还有另一个响应式实现的逻辑,就是利用对象的 get 和 set 函数来进行监听,这种响应式的实现方式,只能拦截某一个属性的修改,这也是 Vue 3 中 ref 这个 API 的实现

在下面的代码中,我们拦截了 count 的 value 属性,并且拦截了 set 操作,也能实现类似的功能

let getDouble = n => n * 2
let _value = 1

double = getDouble(_value)
let count = {
    get value() {
        return _value
    },
    set value(val) {
        _value = val
        double = getDouble(_value)
    }
}

console.log(count.value,double)
count.value = 2
console.log(count.value,double)

三种实现原理的对比表格如下,帮助你理解三种响应式的区别

实现原理 defineProperty Proxy value setter
实际场景 Vue 2 响应式 Vue 3 reactive Vue 3 ref
优势 兼容性 基于proxy实现真正的拦截 实现简单
劣势 数组和属性删除等拦截不了 兼容不了 IE11 只拦截了 value 属性
实际应用 Vue 2 Vue 3 复杂数据结构 Vue 3 简单数据结构

定制响应式数据

简单入门响应式的原理后,接下来我们学习一下响应式数据在使用的时候的进阶方式;我们看下使用 5101c0cdbdc49998c642c71f6b6410a8 重构之后的 todolist 的代码;这段代码使用 watchEffect,数据变化之后会把数据同步到 localStorage 之上,这样我们就实现了 todolist 和本地存储的同步

import { ref, watchEffect, computed } from "vue";
let title = ref("");
let todos = ref(JSON.parse(localStorage.getItem('todos')||'[]'));
watchEffect(()=>{
    localStorage.setItem('todos',JSON.stringify(todos.value))
})
function addTodo() {
    todos.value.push({
        title: title.value,
        done: false,
    });
    title.value = "";
}

更进一步,我们可以直接抽离一个 useStorage 函数,在响应式的基础之上,把任意数据响应式的变化同步到本地存储;我们先看下面的这段代码,ref 从本地存储中获取数据,封装成响应式并且返回,watchEffect 中做本地存储的同步,useStorage 这个函数可以抽离成一个文件,放在工具函数文件夹中

function useStorage(name, value=[]){
    let data = ref(JSON.parse(localStorage.getItem(name)||'[]'))
    watchEffect(()=>{
        localStorage.setItem(name,JSON.stringify(data.value))
    })
    return data
}

在项目中我们使用下面代码的写法,把 ref 变成 useStorage,这也是 Composition API 最大的优点,也就是可以任意拆分出独立的功能

let todos = useStorage('todos',[])
function addTodo() {
    ...code
}

现在,你应该已经学会了在 Vue 内部进阶地使用响应式机制,去封装独立的函数;在后续的实战应用中,我们也会经常对通用功能进行封装;如下图所示,我们可以把日常开发中用到的数据,无论是浏览器的本地存储,还是网络数据,都封装成响应式数据,统一使用响应式数据开发的模式;这样,我们开发项目的时候,只需要修改对应的数据就可以了

Vue3中的响应式机制是什么

基于响应式的开发模式,我们还可以按照类似的原理,把我们需要修改的数据,都变成响应式;比如,我们可以在 loading 状态下,去修改浏览器的小图标 favicon;和本地存储类似,修改 favicon 时,我们需要找到 head 中有 icon 属性的标签

在下面的代码中,我们把对图标的对应修改的操作封装成了 useFavicon 函数,并且通过 ref 和 watch 的包裹,我们还把小图标变成了响应式数据

import {ref,watch} from 'vue'
export default function useFavicon( newIcon ) {
    const favicon = ref(newIcon)
    const updateIcon = (icon) => {
        document.head
        .querySelectorAll(`link[rel*="icon"]`)
        .forEach(el => el.href = `${icon}`)
    }
watch( favicon,
    (i) => {
        updateIcon(i)
    }
)
    return {favicon,reset}
}

这样在组件中,我们就可以通过响应式的方式去修改和使用小图标,通过对 faivcon.value 的修改就可以随时更换网站小图标;下面的代码,就实现了在点击按钮之后,修改了网页的图标为 geek.png 的操作

<script setup>
    import useFavicon from &#39;./utils/favicon&#39;
    let {favicon} = useFavicon()
    function loading(){
        favicon.value = &#39;/geek.png&#39;
    }
</script>
<template>
    <button @click="loading">123</button>
</template>

Vueuse 工具包

我们自己封装的 useStorage,算是把 localStorage 简单地变成了响应式对象,实现数据的更新和localStorage 的同步;同理,我们还可以封装更多的类似 useStorage 函数的其他 use 类型的函数,把实际开发中你用到的任何数据或者浏览器属性,都封装成响应式数据,这样就可以极大地提高我们的开发效率

Vue 社区中其实已经有一个类似的工具集合,也就是 VueUse,它把开发中常见的属性都封装成为响应式函数

VueUse 趁着这一波 Vue 3 的更新,跟上了响应式 API 的潮流;VueUse 的官方的介绍说这是一个 Composition API 的工具集合,适用于 Vue 2.x 或者 Vue 3.x,用起来和 React Hooks 还挺像的

在项目目录下打开命令行里,我们输入如下命令,来进行 VueUse 插件的安装:

npm install @vueuse/core

然后,我们就先来使用一下 VueUse;在下面这段代码中,我们使用 useFullscreen 来返回全屏的状态和切换全屏的函数;这样,我们就不需要考虑浏览器全屏的 API,而是直接使用 VueUse 响应式数据和函数就可以很轻松地在项目中实现全屏功能

<template>
    <h2 @click="toggle">click</h2>
</template>
<script setup>
import { useFullscreen } from &#39;@vueuse/core&#39;
const { isFullscreen, enter, exit, toggle } = useFullscreen()
</script>

useFullscreen 的封装逻辑和 useStorage 类似,都是屏蔽了浏览器的操作,把所有我们需要用到的状态和数据都用响应式的方式统一管理,VueUse 中包含了很多我们常用的工具函数,我们可以把网络状态、异步请求的数据、动画和事件等功能,都看成是响应式的数据去管理。

以上是Vue3中的响应式机制是什么的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:亿速云。如有侵权,请联系admin@php.cn删除
vue.js的功能:增强前端的用户体验vue.js的功能:增强前端的用户体验Apr 19, 2025 am 12:13 AM

Vue.js通过多种功能提升用户体验:1.响应式系统实现数据即时反馈;2.组件化开发提高代码复用性;3.VueRouter提供平滑导航;4.动态数据绑定和过渡动画增强交互效果;5.错误处理机制确保用户反馈;6.性能优化和最佳实践提升应用性能。

vue.js:定义其在网络开发中的作用vue.js:定义其在网络开发中的作用Apr 18, 2025 am 12:07 AM

Vue.js在Web开发中的角色是作为一个渐进式JavaScript框架,简化开发过程并提高效率。1)它通过响应式数据绑定和组件化开发,使开发者能专注于业务逻辑。2)Vue.js的工作原理依赖于响应式系统和虚拟DOM,优化性能。3)实际项目中,使用Vuex管理全局状态和优化数据响应性是常见实践。

了解vue.js:主要是前端框架了解vue.js:主要是前端框架Apr 17, 2025 am 12:20 AM

Vue.js是由尤雨溪在2014年发布的渐进式JavaScript框架,用于构建用户界面。它的核心优势包括:1.响应式数据绑定,数据变化自动更新视图;2.组件化开发,UI可拆分为独立、可复用的组件。

Netflix的前端:React(或VUE)的示例和应用Netflix的前端:React(或VUE)的示例和应用Apr 16, 2025 am 12:08 AM

Netflix使用React作为其前端框架。1)React的组件化开发模式和强大生态系统是Netflix选择它的主要原因。2)通过组件化,Netflix将复杂界面拆分成可管理的小块,如视频播放器、推荐列表和用户评论。3)React的虚拟DOM和组件生命周期优化了渲染效率和用户交互管理。

前端景观:Netflix如何处理其选择前端景观:Netflix如何处理其选择Apr 15, 2025 am 12:13 AM

Netflix在前端技术上的选择主要集中在性能优化、可扩展性和用户体验三个方面。1.性能优化:Netflix选择React作为主要框架,并开发了SpeedCurve和Boomerang等工具来监控和优化用户体验。2.可扩展性:他们采用微前端架构,将应用拆分为独立模块,提高开发效率和系统扩展性。3.用户体验:Netflix使用Material-UI组件库,通过A/B测试和用户反馈不断优化界面,确保一致性和美观性。

React与Vue:Netflix使用哪个框架?React与Vue:Netflix使用哪个框架?Apr 14, 2025 am 12:19 AM

NetflixusesAcustomFrameworkcalled“ Gibbon” BuiltonReact,notReactorVueDirectly.1)TeamExperience:selectBasedAsedonFamiliarity.2)ProjectComplexity:vueforsimplerprojects,vueforsimplerprojects,reactforforforecomplexones.3)cocatizationNeedsneeds:reactofficatizationneedneeds:reactofferizationneedneedneedneeds:reactoffersizatization needeffersefersmoreflexiblesimore.4)ecosyaka

框架的选择:是什么推动了Netflix的决定?框架的选择:是什么推动了Netflix的决定?Apr 13, 2025 am 12:05 AM

Netflix在框架选择上主要考虑性能、可扩展性、开发效率、生态系统、技术债务和维护成本。1.性能与可扩展性:选择Java和SpringBoot以高效处理海量数据和高并发请求。2.开发效率与生态系统:使用React提升前端开发效率,利用其丰富的生态系统。3.技术债务与维护成本:选择Node.js构建微服务,降低维护成本和技术债务。

反应,vue和Netflix前端的未来反应,vue和Netflix前端的未来Apr 12, 2025 am 12:12 AM

Netflix主要使用React作为前端框架,辅以Vue用于特定功能。1)React的组件化和虚拟DOM提升了Netflix应用的性能和开发效率。2)Vue在Netflix的内部工具和小型项目中应用,其灵活性和易用性是关键。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境