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 提供的一个可以拦截对象基础操作 ... 
随机推荐
- PHP做api开发时,签名验证你是怎么设计的
			开发过程中,我们经常会与接口打交道,有的时候是调取别人网站的接口,有的时候是为他人提供自己网站的接口,但是在这调取的过程中都离不开签名验证. 我们在设计签名验证的时候,请注意要满足以下几点: 可变性: ... 
- 线程同步 进程同步 EventWaitHandle
			这个名字LLLLL取相同就能让同一台电脑上两个进程同步 主动控制程序 class Program { static EventWaitHandle eHandle = new EventWaitHan ... 
- 一文读懂Spring的SPI机制
			一. 从类加载说起 Java中的类加载器负载加载来自文件系统.网络或者其他来源的类文件.jvm的类加载器默认使用的是双亲委派模式.三种默认的类加载器Bootstrap ClassLoader.Exte ... 
- Android OpenMAX(四)OMX Core
			假设我们已经写好了所有的OMX组件,有vdec.venc.adec.aenc,接下来问题来了,我们应该如何管理这些组件呢(创建.销毁)?这一篇文章我们向上一层学习OMX Core提供的标准API. O ... 
- 跨域问题服务端解决办法 Request header field Authorization is not allowed by Access-Control-Allow-Headers
			跨域问题服务端解决办法 一般在入口文件加 header('Access-Control-Allow-Origin:*');// 响应类型header('Access-Control-Allow-Met ... 
- 视觉族: 基于Stable Diffusion的免费AI绘画图片生成器工具
			视觉族是一款基于Stable Diffusion文生图模型的免费在线AI绘画图片生成器工具,可以使用提示关键词快速生成精美的艺术图片,支持中文提示.无论你是想要创作自己的原创作品,还是想要为你的文字增 ... 
- 如何5分钟上手使用OCR
			随便打开一个Microsoft Visual Studio,新建一个WinForms项目,从下面列表中随便选择一个NET框架. net35;net40;net45;net451;net452;net4 ... 
- linux获取docker容器中的文件路径怎么表示
			在Linux系统中,Docker容器中的文件路径与宿主机上的文件系统是隔离的,因此我们不能直接使用宿主机的文件系统路径来访问容器内的文件.但是,有几种方法可以让我们获取或操作Docker容器中的文件. ... 
- MySQL学习笔记-多表查询(下)
			多表查询(下) 一. 联合查询 联合查询:将多次查询结果合并,形成新的查询结果集 select {字段列表} from {表A} ... union [all] select {字段列表} from ... 
- 国产大语言模型ChatGLM3本地搭建、使用和功能扩展
			1.官网 ChatGLM3 2.下载ChatGLM3源码 直接在https://github.com/THUDM/ChatGLM3,下载源码 3.下载模型 如果显卡8G一下建议下载ChatGLM3-6 ... 
