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 提供的一个可以拦截对象基础操作 ...
随机推荐
- C# 调用 qrencode的动态库
自己根据qrencode的源码导了一个dll动态库,见: https://www.cnblogs.com/HelloQLQ/p/16364825.html 自己希望能用C#语言调用以下. 首先构建需要 ...
- 高分辨率食道测压(HRM)
高分辨率测压(High resolution Manometry) HRM的优势 高分辨率食管测压不但实现了从咽部到胃部的全程功能监测,而且插管无需牵拉,操作十分方便.更为重要的是,临床医生经过简单的 ...
- Python 潮流周刊#52:Python 处理 Excel 的资源
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- win11或win10客户端邮箱账号登录设置
1.alimall阿里企业邮箱 点击账户 点击添加账户 点击其他账户 输入电子邮箱地址和密码,并点击登录即可 2.Qq邮箱 2.1 点击账户 2.2 点击添加账户 2.3 点击其他账户 2.4 输入电 ...
- Java类加载过程&&静态代码块的初始化过程
问题的引入 还是老规矩,先说说自己遇到的问题. 最近看到了一个比较有意思的Java程序,初次看到这段程序执行的结果还是挺让我意外的,话不多说先上程序,大家也可以揣摩一下(大神自行略过......) c ...
- k8s 怎么精准获取deployment关联的pods?
标签获取 我们获取那些pods属于某个deployment时最先想到的可能是通过标签获取,其实这个是不准确的.因为标签并不是唯一的,也就是说不同deployment其实是能有相同标签的. replic ...
- Android 13 - Media框架(2)- Demo App与MediaPlayer Api了解
关注公众号免费阅读全文,进入音视频开发技术分享群! 尝试用MediaPlayer写了一个播放demo,实现了网络流和本地流的播放.由于本人对app开发一窍不通,所以demo中很多内容是边查资料边写的, ...
- H264 H265 分析小工具
1.在调试 H264 H265 编码的流数据的时候,有时候需要打印没有nalu的类型和数量,自己写了一个小工具 使用方式: p.p1 { margin: 0; font: 22px Menlo; co ...
- MySQL创建表的时候建立联合索引的方法
1.MySQL创建表建立联合索引的步骤 在MySQL中,联合索引(也称为复合索引或多列索引)是基于表中的多个列创建的索引.这种索引可以提高多列查询的性能,特别是当查询条件涉及这些列时.下面是一个详细的 ...
- mongodb常用数据库指令
通过客户端的命令进入到mongodb服务中 mongo命令进入客户端 show dbs 查看数据库 show tables/show collections 查看集合(查看当前库里面的表) db 查 ...