巧妙的响应式:深入理解Vue 3的响应式机制
注:本文是大圣老师课程的笔记,原课程地址:07 | 巧妙的响应式:深入理解Vue 3的响应式机制
一,什么是响应式
看下面的代码,double依赖于count,但是当我们修改了count,double却没有发生对应的变化。
let count = 1
let double = count * 2
console.log(double)//2
count = 2
console.log(double)//2
想要实现count变化后,double自动更新。就需要一个东西去实时监听count,当发现count变化了,就再次调用double = count * 2更新下。如下:
创建一个监听的对象object
let count = 1 //object开始监听count
let double = count * 2
console.log(double)//2
count = 2 //object监听count到count发生变化了。就再次调用double = count * 2,更新double
console.log(double)//2,实际上我们想要的是它变成4,这个操作应该是监听者object再次调用double = count * 2实现
二,响应式的原理
Vue 中用过三种响应式解决方案,分别是 defineProperty、Proxy 和 value setter。
我们首先来看 Vue 2 的 defineProperty API,这个函数详细的 API 介绍你可以直接访问MDN 介绍文档Object.defineProperty() - JavaScript | MDN (mozilla.org)来了解。
在下面的代码中,我们定义个一个对象 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 = valdouble = getDouble(val)}
})//这个东西就是上文中说的监听者
console.log(double) // 打印2
obj.count = 2
console.log(double) // 打印4 有种自动变化的感觉
这样我们就实现了简易的响应式功能。
但 defineProperty API 作为 Vue 2 实现响应式的原理,它的语法中也有一些缺陷。比如在下面代码中,我们删除 obj.count 属性(删除的是堆空间中的obj.count),set 函数就不会执行,double 还是之前的数值(double还是全局变量)。这也是为什么在 Vue 2 中,我们需要 $delete 一个专门的函数去删除数据。
delete obj.count
console.log(double) // doube还是4
而Vue 3 的响应式机制是基于 Proxy 实现的,Proxy 的重要意义在于它解决了 Vue 2 响应式的缺陷。
Proxy和Math,Date一样,是js的内置对象。
我们看下面的代码,在其中我们通过 new Proxy 代理了 obj 这个对象,然后通过 get、set 和 deleteProperty 函数代理了对象的读取、修改和删除操作,从而实现了响应式的功能。
let obj = {}
let count = 1
let getDouble = n=>n*2
let double = getDouble(count)
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}}
})
proxy.count=count
console.log(obj.count,double)//1 2
proxy.count = 2
console.log(obj.count,double) //2 4
delete proxy.count
// 删除属性后,我们打印log时,输出的结果就会是 undefined NaN
console.log(obj.count,double) //undefined NaN
我们从这里可以看出 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, watchEffect, computed } from "vue";
let obj = reactive({count: 1
});
let double = computed(() => obj.count * 2);
setTimeout(() => {obj.count = 2;
}, 3000);
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 = valdouble = getDouble(_value)}
}
console.log(count.value,double)
count.value = 2
console.log(count.value,double)
三种实现原理的对比表格如下:
三,定制响应式数据
setup重构之后的 todolist 的代码。这段代码使用 watchEffect,数据变化之后会把数据同步到 localStorage 之上,这样我们就实现了 todolist 和本地存储的同步。
function useStorage(name, value=[]){let data = ref(JSON.parse(localStorage.getItem(name)|| value))watchEffect(()=>{localStorage.setItem(name,JSON.stringify(data.value))})return data
}
更进一步,我们可以直接抽离一个 useStorage 函数,在响应式的基础之上,把任意数据响应式的变化同步到本地存储。我们先看下面的这段代码,ref 从本地存储中获取数据,封装成响应式并且返回,watchEffect 中做本地存储的同步,useStorage 这个函数可以抽离成一个文件,放在工具函数文件夹中。
function useStorage(name, value=[]){let data = ref(JSON.parse(localStorage.getItem(name)|| value))watchEffect(()=>{localStorage.setItem(name,JSON.stringify(data.value))})return data
}
在项目中我们使用下面代码的写法,把 ref 变成 useStorage,这也是 Composition API 最大的优点,也就是可以任意拆分出独立的功能。
let todos = useStorage('todos',[])function addTodo() {...code
}
我们可以把日常开发中用到的数据,无论是浏览器的本地存储,还是网络数据,都封装成响应式数据,统一使用响应式数据开发的模式。这样,我们开发项目的时候,只需要修改对应的数据就可以了。
四,reactive 正确使用姿势
reactive函数传递的参数必须是对象(json/arr)千万不要这样写 let objData=reactive({ name:'林漾', age:31, sex:'女' })这样写是非常的不好的。有的小伙伴可能会说:上面reactive函数传递的参数是对象呀有什么问题了???问题是在我们跟新数据的时候一点都不友好还有就是我们在实际开发过程中可能有好几处都是响应式的数据这个时候我们只需要创建一个reactive就说按照下面这样来处理let objData=reactive({// 某一个区域使用的数据oneObj:[{name:'林漾',age:31,sex:'女'}],// 另一个区域使用的数据two:{name:'余声声',age:123}
});不推荐这样使用
let oneObj=reactive({name:'林漾',age:31,sex:'女'
})let two=reactive({name:'余声声',age:123
})
五,Vueuse 工具包
我们自己封装的 useStorage,算是把 localStorage 简单地变成了响应式对象,实现数据的更新和 localStorage 的同步。同理,我们还可以封装更多的类似 useStorage 函数的其他 use 类型的函数,把实际开发中你用到的任何数据或者浏览器属性,都封装成响应式数据,这样就可以极大地提高我们的开发效率。
Vue 社区中其实已经有一个类似的工具集合,也就是 VueUse,它把开发中常见的属性都封装成为响应式函数。
vueuse的安装:
npm install @vueuse/core
简单使用:
import { useMouse } from "@vueuse/core";
// "x" and "y" are refs
const { x, y } = useMouse();
console.log( x.value, y.value);
巧妙的响应式:深入理解Vue 3的响应式机制相关推荐
- vue 数组长度_深入理解Vue的数据响应式
什么是响应式 当一个物体对外界的变化做出反应就叫响应式的,如"我打你一拳,你会喊疼". Vue的数据响应式 就是对数据做出改变时,视图上也会做出相应的变化. 举个例子 1const ...
- data的值 如何初始化vue_理解Vue响应式系统
深入理解 Vue 响应式系统 理解 Vue 响应式原理,到 computed.vuex 原理 前言 众所周知,一说到 vue 的响应式系统,就能马上想到 Object.defineProperty.数 ...
- 详细介绍Vue的数据响应式
Vue.js的核心包括一套"响应式系统"."响应式",是指当数据改变后,Vue会通知到使用该数据的代码.例如,视图渲染中使用了数据,数据改变后,视图也会自动更新 ...
- 理解Vue深度响应原理
Vue核心原理之数据的深度响应 1.问题的引入 为什么点击下面的button界面会出现自增? <div id="example-2"> <simple-count ...
- 彻底理解Vue数据响应式原理
彻底理解Vue数据响应式原理 当创建了一个实例后,通过将数据传入实例的data属性中,会将数据进行劫持,实现响应式,也就是说当这些数据发生变化时,对应的视图也会发生改变. const data = { ...
- 深入理解Vue响应式原理
前言 Vue响应式原理是Vue最独特的特性之一,当数据模型进行修改时,视图就会进行更新,这使得状态管理简单直接,但是其底层的细节还是需要我们深入学习理解,这样遇到一些问题我们才能快速进行定位,并解决: ...
- Vue数据绑定和响应式原理
Vue数据绑定和响应式原理 当实例化一个Vue构造函数,会执行 Vue 的 init 方法,在 init 方法中主要执行三部分内容,一是初始化环境变量,而是处理 Vue 组件数据,三是解析挂载组件.以 ...
- 深入剖析Vue源码 - 响应式系统构建(上)
从这一小节开始,正式进入Vue源码的核心,也是难点之一,响应式系统的构建.这一节将作为分析响应式构建过程源码的入门,主要分为两大块,第一块是针对响应式数据props,methods,data,comp ...
- Vue.js 深入响应式原理
深入响应式原理 现在是时候深入一下了!Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的 JavaScript 对象.而当你修改它们时,视图会进行更新.这使得状态管理非常简单直接 ...
- Vue 项目前端响应式布局及框架搭建
Vue 项目前端响应式布局及框架搭建 一.flexible 插件 1.引用 flexible 插件 2.修改 flexible 默认配置 3.展示效果 二.cssrem 插件 (px -> re ...
最新文章
- T-SQL查询——详解公用表达式(CTE)
- c# Bitmap byte[] Stream 文件相互转换
- python【Matlibplot绘图库】曲线样式的两种设置方式(看不懂你来打我)
- 小白学习机器学习---第六章:SVM算法原理(1)
- 数据结构与算法之完全二叉树的节点个数
- 【图像处理】——图像增强Python实现直方图均衡化
- python堆栈反向输出列表_python - IPython:将Python脚本的输出重定向到文件(如bash) - 堆栈内存溢出...
- 湖北大学计算机袁云,暑期走访不停歇 远赴异地送关怀——学校慰问离退休教职工和校友...
- idea新建springboot后端到前端_基于SpringBoot+AntDesign的快速开发平台
- CodeIgniter 的数据安全过滤全解析
- 黒猩猩盗猎越来越严重!新科技「猩脸辨识」技术诞生
- Rational RequisitePro
- matlab拟合曲线教程,【Matlab】matlab如何使用拟合工具?matlab如何拟合曲线?matlab拟合工具cftool如何使用?...
- 微雪树莓派PICO笔记——8-PIO(可编程输入输出接口)
- 计算机科学与技术考研吧,计算机科学与技术考研学校排名
- 1050: 找出直系亲属
- Kruskal重构树 学习笔记
- YoLoV5学习(4)--detect.py程序(预测图片、视频、网络流)逐段讲解~
- 基于Vue的微信公众号开发及选型研究
- 非线性方程的数值解法
热门文章
- 2020教师计算机考试笔试题,2020年全国教师资格考试信息技术学科知识与能力练习题...
- linux内核计算list的长度,linux内核list.h头文件分析(四)
- springboot中Word转PDF
- Lucene PriorityQueue JDK PriorityQueue
- ArrayUtils
- 一道SQL题考你数据库的使用能力
- Ajax实现动态的二级级联菜单
- DFX 9.303 for QQMusic 2010
- C语言中malloc,calloc,realloc,free的语法与作用
- mybatis plus+spring boot 多租户动态数据源实现方案