Vue3 之 reactive、ref、toRef、toRefs 使用与区别,源码分析详细注释
reactive、ref、toRef、toRefs 使用与区别
reactive
- 参数传入普通对象,不论层级多深都可以返回响应式对象,(参数只能是对象)
- 但是解构、扩展运算符会失去响应式
ref 作用及用法
- 参数可以为任意类型,推荐使用基本类型
- 使用时 需要通过
xxx.value的形式获取 - 本质是
拷贝粘贴一份数据,脱离了与源数据的交互 - 将对象中属性变成响应式数据,修改该数据是不会影响到源数据,但是会更新视图
<template>
<div id="contain">
{{ refTest }}
<button @click="change">click</button>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
const hello = "hello"; // 源数据
const refTest = ref(hello); // 相当于复制了一份
function change() {
refTest.value = "world"; // 会更新视图
console.log(refTest);
console.log(hello); // 源数据不变 hello
}
</script>
toRef 作用及用法
- 针对 reactive 解构失去响应式的问题,创建了 toRef,用于为源响应式对象上的属性新建一个 ref,保持对源对象属性的响应式交互。
- 语法:
toRef(target, key) - 使用时 需要通过
xxx.value的形式获取 - 本质是引用,与源数据有交互,修改该数据是会影响源数据,但是不会更新视图,如果需要更新视图,需要使用 reactive 包裹源数据
<template>
<div id="contain">
<p>{{ toRefTest }} <button @click="changeNor">click</button></p>
<p>{{ toRefReactTest }} <button @click="changeRea">click</button></p>
</div>
</template>
<script lang="ts" setup>
import { reactive, toRef } from "vue";
// 普通对象
const state = { name: "hello" }; // 源数据
const toRefTest = toRef(state, "name"); // toRef 本质是引用
function changeNor() {
toRefTest.value = "world"; // 不会更新视图
console.log(state); // 会影响源数据
}
// ------------------------------------------
// reactive 包裹源数据
const reactState = reactive({ name: "hello" }); // reactive 包裹源数据
const toRefReactTest = toRef(reactState, "name");
function changeRea() {
toRefReactTest.value = "world"; // 会更新视图
console.log(reactState); // 会影响源数据
}
</script>
toRefs 作用及用法
- 就是批量的 toRef 操作,toRefs 是一次性将 reactive 中的所有属性都转为 ref
- 语法:
toRefs(target) - 同样可以用于解构 reactive 的响应式对象
- 使用时 需要通过
xxx.value的形式获取 - 本质是引用,与源数据有交互,修改该数据是会影响源数据,但是不会更新视图,如果需要更新视图,需要使用 reactive 包裹源数据
<template>
<div id="contain">
<p>
普通对象:{{ name }} -- {{ age }}
<button @click="changeNor">click</button>
</p>
<p>
reactive对象:{{ rename }} -- {{ reage
}}<button @click="changeRea">click</button>
</p>
</div>
</template>
<script lang="ts" setup>
import { reactive, toRefs } from "vue";
// 普通对象
const state = { name: "hello", age: 18 }; // 源数据
const { name, age } = toRefs(state); // 本质是引用
function changeNor() {
name.value = "world"; // 不会更新视图
age.value++;
console.log(state); // 会影响源数据
}
// ------------------------------------------
// reactive 包裹源数据
const reactState = reactive({ rename: "hello", reage: 18 }); // reactive 包裹源数据
const { rename, reage } = toRefs(reactState); // 本质是引用
function changeRea() {
rename.value = "world"; // 会更新视图
reage.value++;
console.log(reactState); // 会影响源数据
}
</script>
ref,toRef,toRefs 源码实现解析详细注释
ref 和 reactive 的底层原理区别: reactive 内部采用 proxy ,而 ref 中内部采用的是 defineProperty
ref、shallowRef 源码实现。使用class RefImpl实现,会被 babel 编译成defineProperty
其中 share.ts 和 reactive.ts 文件中的方法,不在赘述,详见上一篇 响应式原理的文章
import { hasChanged, isArray, isObject } from "./shared";
import { TrackOpTypes, TriggerOrTypes } from "./shared";
import { track, trigger } from "./effect";
import { reactive } from "./reactive";
export function ref(value) {
return createRef(value);
}
// shallowRef 只能处理基本类型数据
export function shallowRef(value) {
return createRef(value, true);
}
const convert = (val) => (isObject(val) ? reactive(val) : val);
class RefImpl {
public _value;
public __v_isRef = true; // 实例添加 __v_isRef, 表示是一个ref属性
constructor(public rawValue, public shallow) {
// 1.参数前面加修饰符 表示实例属性
this._value = shallow ? rawValue : convert(rawValue); // 如果是深度监听 需要把里面的都变成响应式的
}
// 2. 类的属性访问器
get value() {
// 3. 依赖收集 代理取值取_value
track(this, TrackOpTypes.GET, "value"); // 依赖收集
return this._value;
}
set value(newValue) {
// 4. 判断老值和新值是否有变化
if (hasChanged(newValue, this.rawValue)) {
this.rawValue = newValue; // 新值会作为老值
this._value = this.shallow ? newValue : convert(newValue);
// 5. 触发更新
trigger(this, TriggerOrTypes.SET, "value", newValue);
}
}
}
function createRef(rawValue, shallow = false) {
return new RefImpl(rawValue, shallow);
}
toRef,toRefs 源码实现。使用class ObjectRefImpl实现,会被 babel 编译成defineProperty
class ObjectRefImpl {
public __v_isRef = true; // 实例添加 __v_isRef, 表示是一个ref属性
constructor(public target, public key) {}
get value() {
return this.target[this.key]; // 如果原对象是响应式的就会依赖收集
}
set value(newValue) {
this.target[this.key] = newValue; // 如果原来对象是响应式的 那么就会触发更新
}
}
// 将对象的某一个值转化成 ref类型
export function toRef(target, key) {
return new ObjectRefImpl(target, key);
}
// 可能传递的是一个数组 或者对象,属性全部转化成 ref类型
export function toRefs(object) {
const ret = isArray(object) ? new Array(object.length) : {};
for (let key in object) {
ret[key] = toRef(object, key); // 遍历调用toRef方法
}
return ret;
}
Vue3 之 reactive、ref、toRef、toRefs 使用与区别,源码分析详细注释的更多相关文章
- Vue3中的响应式对象Reactive源码分析
Vue3中的响应式对象Reactive源码分析 ReactiveEffect.js 中的 trackEffects函数 及 ReactiveEffect类 在Ref随笔中已经介绍,在本文中不做赘述 本 ...
- Vue.js 源码分析(十) 基础篇 ref属性详解
ref 被用来给元素或子组件注册引用信息.引用信息将会注册在父组件的 $refs 对象上.如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素:如果用在子组件上,引用就指向组件实例,例如: ...
- Vue3源码分析之 Ref 与 ReactiveEffect
Vue3中的响应式实现原理 完整 js版本简易源码 在最底部 ref 与 reactive 是Vue3中的两个定义响应式对象的API,其中reactive是通过 Proxy 来实现的,它返回对象的响应 ...
- Vue3源码分析之Diff算法
Diff 算法源码(结合源码写的简易版本) 备注:文章后面有详细解析,先简单浏览一遍整体代码,更容易阅读 // Vue3 中的 diff 算法 // 模拟节点 const { oldVirtualDo ...
- Vue3源码分析之微任务队列
参考资料:https://zh.javascript.info/microtask-queue#wei-ren-wu-dui-lie-microtaskqueue 简化版 Vue3 中的 微任务队列实 ...
- Spring boot加载REACTIVE源码分析
一,加载REACTIVE相关自动配置 spring boot通过判断含org.springframework.web.reactive.DispatcherHandler字节文件就确定程序类型是REA ...
- petite-vue源码剖析-逐行解读@vue/reactivity之reactive
在petite-vue中我们通过reactive构建上下文对象,并将根据状态渲染UI的逻辑作为入参传递给effect,然后神奇的事情发生了,当状态发生变化时将自动触发UI重新渲染.那么到底这是怎么做到 ...
- Vue3全局APi解析-源码学习
本文章共5314字,预计阅读时间5-15分钟. 前言 不知不觉Vue-next的版本已经来到了3.1.2,最近对照着源码学习Vue3的全局Api,边学习边整理了下来,希望可以和大家一起进步. 我们以官 ...
- ref、reactive、toRef、toRefs使用与区别
reactive 传参:reactive(arg),arg只能是对象 arg为普通对象 返回响应式对象,不管层级多深,都能响应 使用:获取数据值的时候直接获取,不需要加.value 特点:解构.扩展运 ...
- 全面了解Vue3的 reactive 和相关函数
Vue3的 reactive 怎么用,原理是什么,官网上和reactive相关的那些函数又都是做什么用处的?这里会一一解答. ES6的Proxy Proxy 是 ES6 提供的一个可以拦截对象基础操作 ...
随机推荐
- SpringMVC 项目集成 PageOffice V6 最简单代码
本文描述了PageOffice产品在SpringMVC项目中如何集成调用. 新建SpringMVC项目:pageoffice6-springmvc-simple 在您项目的pom.xml中通过下面的代 ...
- 智能便捷_AIRIOT智慧充电桩管理解决方案
现如今随着对可持续交通的需求不断增加,电动车市场正在迅速扩大,建设更多更智能的充电桩,并通过管理平台提高充电设施的可用性和效率成为一项重要任务.传统的充电桩管理平台在对充电设施进行管理过程中,存在如下 ...
- linux 文件扩展权限ACL(访问控制列表)
目录 一.关于文件扩展权限ACL 二.给文件加扩展权限 三.给目录加扩展权限 四.给目录下所有文件都加扩展权限 五.去掉单个acl权限 六.去掉所有acl权限 一.关于文件扩展权限ACL 在linux ...
- 文件系统(五):exFAT 文件系统原理详解
前言 exFAT是微软2006年推出的一种文件系统,距今已快二十年,相比于FAT16和FAT32,exFAT还是算年轻.exFAT一直是微软的一个专用文件系统,直到2019年微软发布它的规范,目前微软 ...
- k8s——pod探针
探针 简单理解: 容器内应用的检测机制,根据不同的探针来判断容器应用当前的状态 为什么会需要探针 # 情况一 现在有一个商品的微服务,跑着跑着突然内存溢出,程序崩掉了,外面的pod虽然在,但是也相当于 ...
- php分组查询和聚合函数 数据显示的顺序
// 分组查询和聚合函数 // 分组查询 : 将指定字段中的数据,按照不同的具体数值,进行分组 // 数据相同的分在一个分组中 ...
- 自定义动画 jquery的结束动画
<button name="width">改变宽</button> <button name="height"> ...
- C# .NET HttpWebRequest 按每个(单个)请求跳过证书校验
C# .NET HttpWebRequest 按每个(单个)请求跳过证书校验 自签名证书 HTTPS TLS . 使用.NET 4.5 新加的属性 HttpWebRequest.ServerCerti ...
- rust程序设计(4)关于 trait | impl 相关的概念和疑问
trait是什么? Rust中的trait是一种定义可被多种类型实现的共享行为的方式.它类似于Java或C#中的接口.通过trait,你可以定义一组方法签名(有时包括默认实现),不同的类型可以实现这些 ...
- Postman 的 Basic Auth 如何通过 Feign 实现
Postman 的 Basic Auth: 分析 根据以上图片分析: Postman 的 Authorization 实际为: header 中添加 Authorization: ******* ** ...