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 使用与区别,源码分析详细注释的更多相关文章

  1. Vue3中的响应式对象Reactive源码分析

    Vue3中的响应式对象Reactive源码分析 ReactiveEffect.js 中的 trackEffects函数 及 ReactiveEffect类 在Ref随笔中已经介绍,在本文中不做赘述 本 ...

  2. Vue.js 源码分析(十) 基础篇 ref属性详解

    ref 被用来给元素或子组件注册引用信息.引用信息将会注册在父组件的 $refs 对象上.如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素:如果用在子组件上,引用就指向组件实例,例如: ...

  3. Vue3源码分析之 Ref 与 ReactiveEffect

    Vue3中的响应式实现原理 完整 js版本简易源码 在最底部 ref 与 reactive 是Vue3中的两个定义响应式对象的API,其中reactive是通过 Proxy 来实现的,它返回对象的响应 ...

  4. Vue3源码分析之Diff算法

    Diff 算法源码(结合源码写的简易版本) 备注:文章后面有详细解析,先简单浏览一遍整体代码,更容易阅读 // Vue3 中的 diff 算法 // 模拟节点 const { oldVirtualDo ...

  5. Vue3源码分析之微任务队列

    参考资料:https://zh.javascript.info/microtask-queue#wei-ren-wu-dui-lie-microtaskqueue 简化版 Vue3 中的 微任务队列实 ...

  6. Spring boot加载REACTIVE源码分析

    一,加载REACTIVE相关自动配置 spring boot通过判断含org.springframework.web.reactive.DispatcherHandler字节文件就确定程序类型是REA ...

  7. petite-vue源码剖析-逐行解读@vue/reactivity之reactive

    在petite-vue中我们通过reactive构建上下文对象,并将根据状态渲染UI的逻辑作为入参传递给effect,然后神奇的事情发生了,当状态发生变化时将自动触发UI重新渲染.那么到底这是怎么做到 ...

  8. Vue3全局APi解析-源码学习

    本文章共5314字,预计阅读时间5-15分钟. 前言 不知不觉Vue-next的版本已经来到了3.1.2,最近对照着源码学习Vue3的全局Api,边学习边整理了下来,希望可以和大家一起进步. 我们以官 ...

  9. ref、reactive、toRef、toRefs使用与区别

    reactive 传参:reactive(arg),arg只能是对象 arg为普通对象 返回响应式对象,不管层级多深,都能响应 使用:获取数据值的时候直接获取,不需要加.value 特点:解构.扩展运 ...

  10. 全面了解Vue3的 reactive 和相关函数

    Vue3的 reactive 怎么用,原理是什么,官网上和reactive相关的那些函数又都是做什么用处的?这里会一一解答. ES6的Proxy Proxy 是 ES6 提供的一个可以拦截对象基础操作 ...

随机推荐

  1. PHP做api开发时,签名验证你是怎么设计的

    开发过程中,我们经常会与接口打交道,有的时候是调取别人网站的接口,有的时候是为他人提供自己网站的接口,但是在这调取的过程中都离不开签名验证. 我们在设计签名验证的时候,请注意要满足以下几点: 可变性: ...

  2. 线程同步 进程同步 EventWaitHandle

    这个名字LLLLL取相同就能让同一台电脑上两个进程同步 主动控制程序 class Program { static EventWaitHandle eHandle = new EventWaitHan ...

  3. 一文读懂Spring的SPI机制

    一. 从类加载说起 Java中的类加载器负载加载来自文件系统.网络或者其他来源的类文件.jvm的类加载器默认使用的是双亲委派模式.三种默认的类加载器Bootstrap ClassLoader.Exte ...

  4. Android OpenMAX(四)OMX Core

    假设我们已经写好了所有的OMX组件,有vdec.venc.adec.aenc,接下来问题来了,我们应该如何管理这些组件呢(创建.销毁)?这一篇文章我们向上一层学习OMX Core提供的标准API. O ...

  5. 跨域问题服务端解决办法 Request header field Authorization is not allowed by Access-Control-Allow-Headers

    跨域问题服务端解决办法 一般在入口文件加 header('Access-Control-Allow-Origin:*');// 响应类型header('Access-Control-Allow-Met ...

  6. 视觉族: 基于Stable Diffusion的免费AI绘画图片生成器工具

    视觉族是一款基于Stable Diffusion文生图模型的免费在线AI绘画图片生成器工具,可以使用提示关键词快速生成精美的艺术图片,支持中文提示.无论你是想要创作自己的原创作品,还是想要为你的文字增 ...

  7. 如何5分钟上手使用OCR

    随便打开一个Microsoft Visual Studio,新建一个WinForms项目,从下面列表中随便选择一个NET框架. net35;net40;net45;net451;net452;net4 ...

  8. linux获取docker容器中的文件路径怎么表示

    在Linux系统中,Docker容器中的文件路径与宿主机上的文件系统是隔离的,因此我们不能直接使用宿主机的文件系统路径来访问容器内的文件.但是,有几种方法可以让我们获取或操作Docker容器中的文件. ...

  9. MySQL学习笔记-多表查询(下)

    多表查询(下) 一. 联合查询 联合查询:将多次查询结果合并,形成新的查询结果集 select {字段列表} from {表A} ... union [all] select {字段列表} from ...

  10. 国产大语言模型ChatGLM3本地搭建、使用和功能扩展

    1.官网 ChatGLM3 2.下载ChatGLM3源码 直接在https://github.com/THUDM/ChatGLM3,下载源码 3.下载模型 如果显卡8G一下建议下载ChatGLM3-6 ...