您当前的位置:首页 > 电脑百科 > 程序开发 > 前端

vue3中 ref和 reactive的区别 ?

时间:2024-01-03 14:28:41  来源:今日头条  作者:互联网高级架构师

最近有朋友在面试过程中经常被问到这么一个问题,vue3 中的ref 和 reactive的区别在哪里,为什么 要定义两个API 一个 api不能实现 响应式更新吗??

带着这个疑问 ,我们 接下来进行逐一探讨。

1 : 分析

1.1 ref and reactive 怎么用 ?

相信大家都知道在vue3中我们可以通过一些api去定义响应式数据,比如 ref, reactive ,shallowRef.

ref 基本使用

<template>
    <div>
        <span>{{inner.content.text}}</span>
         <span>{{count}}</span>
    </div>
 </template>
<script setup>
const inner = ref({
  content: {
    text: "内部内容"
  }
}); // 你可以通过 ref 定义复杂数据类型
// or
const count = ref(20); //  定义普通数据类型
</script>

 

reactive 基本使用

<template>
  <div>
    <div>{{wApper.subText}}</div>
    <div v-for="item in list" :key="item.id">{{item.content}}</div>
  </div>
</template>
<script setup>
const wapper = reactive({
  subText: inner
});
const list = reactive([
  {
    id: 1,
    content: "render"
  },
  {
    id: 2,
    content: "render2"
  }
]);
</script>

 

当然你还可以配合 computed od watchEffec使用 这里我就不再过多介绍了。

1.2 ref 和 reactive 的区别?

相信大家读到这里可以看出 ref 既可以定义基本数据类型 也可以 定义复杂数据类型,而reactive只定义复杂数据类型。

那有人就问了 ? reactive 只能存 复杂数据类型吗?

答案很明显不是的 reactive也可以存基本数据类型

那他们到底区别在哪里呢? 我想这个时候 从我们开发者的角度上没办法看出本质的区别,无非是定义变量呗,那接下来请随者我一起进入源码的是世界。

1.3 源码实现流程 ?

1.3.1 如何找到源码?

先回答第一个问题,怎么找源码,这个需要你对源码的包非常熟悉 我们可以通过看package.json文件先找到它打包的入口文件,然后再去根据不同的情况找不同的文件。

1.3.2 : 找到ref函数的源码文件 ,看看函数内部做了什么事情?

源码文件 : corepackagesreactivitysrcref.ts

ref.ts

核心代码实现

// ref.ts 文件93 行 
export function ref(value?: unknown) {
  return createRef(value, false) //1 : 提供 ref函数 , false 是否浅复制
}
// ref.ts文件第 127行  
// 调用 ref 返回一个 创建 的方法 createRef 传入 两个值 
/**
 * @param rawValue ref函数传入的参数
 * @param shallow 是否浅复制
 */
function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) { // 是否是ref对象 如果是 则 直接返回
    return rawValue
  }
  return new RefImpl(rawValue, shallow) // 否则 创建 ref 对象 传入 rawValue shallow
}


// ref.ts 文件第 134行 
class RefImpl<T> { // 创建一个 ref的 实现类 
  private _value: T // 创建私有的 _value 变量 
  private _rawValue: T // 创建私有的 _rawValue 变量 

  public dep?: Dep = undefined // 是否 dep 
  public readonly __v_isRef = true // 只读的 属性 是否是 ref 

  constructor(value: T, public readonly __v_isShallow: boolean) {
    // 实例被 new时 执行 constructor 保存 传入的值
    this._rawValue = __v_isShallow ? value : toRaw(value) // 是否浅复制 , 如果时 则直接返回 传入的值 否则进行 获取其原始对象
        this._value = __v_isShallow ? value : toReactive(value) // 是否浅复制 是 返回原value 否则 转换成 reactive 对象
  }

  get value() { // 获取值的时候 直接将 constructor 保存的值 返回 
    trackRefValue(this) // 跟踪 ref 的 value
    return this._value  // 获取value 是 返回 _value 对象
  }

  set value(newVal) {// 当 设置值的时候 往下看 
  
  // 是否浅复制 or 值身上是否有 __v_isShallow 标识 or 是否是只读的 标识__v_isReadonly
    const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal); 
    // 如果满足 则 返回新设置的值  , 如果不是 则 取出 新值的原始对象 
    newVal = useDirectValue ? newVal : toRaw(newVal)  // 如果 你一个 浅层对象(普通数据类型) 则 原值返回 否则 判断是否能从 代理对象中 取出源值
    if (hasChanged(newVal, this._rawValue)) { // 判断对象是否发生 变化 变了向下走  
      this._rawValue = newVal // 将最新值 赋给 _rawValue
      this._value = useDirectValue ? newVal : toReactive(newVal) // 判断是否是基本数据类型  如果 是 则 将最新值返回 否则 继续转换 reactive
      triggerRefValue(this, newVal) // 触发 ref 的 value 值进行监听更新
    }
  }
}
// 判断是否 是对象 如果是 则 reactive代理 否则 返回 当前的value
export const toReactive = <T extends unknown>(value: T): T => 
isObject(value) ? reactive(value) : value

 

以上代码就是 ref 的核心实现 , 相信看来好像源码也没有那么难。

1.3.3.总结一下 ref做了什么?

  1. 调用ref将 定义的数据传入,返回一个创建ref响应式数据的函数,是否需要浅层复制,默认为false,也就意味着一定会走 toReactive
  2. 调用createRef, 判断是否 是一个 ref对象 ,是 原值返回 否则 , new 一个 实现ref类
  3. 创建类的私有变量 ,保存传入的value 和 shallow
  4. 判断是否浅层复制,如果是则 返回传入的 value,否则取出 ref的原始值对象
  5. 获取值的时候将 保存的值 返回 出去
  6. 设置值的时候 判断当前属性 是否是浅层对象 ,如果是 则返回该数据 否则 调用 toreactive转换 reactive 进行操作,如果是 普通数据类型,原值返回,按照 defineProperty 进行处理
  7. 触发更新

1.3.4 : 找到reactve函数的源码文件 ,看看函数内部做了什么事情?

reactive.ts

export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (isReadonly(target)) { // 如果是 只读的 不允许 写入 则返回只读对象
    return target
  }
  return createReactiveObject( // 返回一个创建 reactive 的对象
    target, // 传入 目标对象
    false, // 是否是只读对象 
    mutableHandlers, //提供 get, set, deleteProperty, has, ownKeys 方法
    mutableCollectionHandlers, // 太多了 自己看源码 
    reactiveMap // 提供一个 weakmap 集合
    
  )
}
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any> 
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) {  // 如果不是一个对象 则 返回当前 traget 
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // target is already a Proxy, return it.
  // exception: calling readonly() on a reactive object
  if (
    target[ReactiveFlags.RAW] && // 如果target 已经是一个 代理对象 则 返回当前对象
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  const existingProxy = proxyMap.get(target) // 如果对象已经有了代理对象 则直接取值 返回 
  if (existingProxy) {
    return existingProxy
  }
  // only specific value types can be observed.
  const targetType = getTargetType(target) // 观察指定类型
  if (targetType === TargetType.INVALID) {
    return target
  }
  const proxy = new Proxy( // 将对象进行代理 
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy) // 设置目标为代理对象 
  return proxy // 将对象返回出去
}  

 

1.3.5.总结一下 reactive做了什么?

  1. 调用reactive方法 传数据,判断 对象是否是只读对象 如果是 直接返回 无法操作 否则向下继续执行
  2. 调用 createReactiveObject 返回一个代理对象
  3. 判断传入的值是不是一个对象 如果不是则直接原路返回,否则判断target 是不是一个 已经代理过了的对象
  4. 如果是代理过的对象 原路返回 否则 判断目标对象是否有相应代理有直接取出响应代理对象 否则 继续向下
  5. 针对不同的 值类型处理
  6. 代理整个trarget 我,将当前代理的对象设置到weakmap 中 将代理完的对象返回

1.4 总结

从源码的角度来说 ref本身 会判断是否为 基本数据类型 如果是 则是defineProperty 实现的如果是复杂数据类型就会按照 reactive进行处理 ,针对不同的数据类型会进行操作,当你ref为 对象时会转换 reactive对象 将代理的对象 返回给 _value对象,如果是基本数据则会判断是否需要浅层复制,不需要则直接返回了。 而 reactive 这边也会判断 是不是 基本数据类型 是 直接返回 否则就直接将对象进行了代理并返回。相信本篇文章能够给你带来一些启发。

 

作者:前端小张同学
链接:
https://juejin.cn/post/7263411272892825655



Tags:vue3   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
SpringBoot3+Vue3 开发高并发秒杀抢购系统
开发高并发秒杀抢购系统:使用SpringBoot3+Vue3的实践之旅随着互联网技术的发展,电商行业对秒杀抢购系统的需求越来越高。为了满足这种高并发、高流量的场景,我们决定使用Spring...【详细内容】
2024-01-14  Search: vue3  点击:(90)  评论:(0)  加入收藏
vue3中 ref和 reactive的区别 ?
最近有朋友在面试过程中经常被问到这么一个问题,vue3 中的ref 和 reactive的区别在哪里,为什么 要定义两个API 一个 api不能实现 响应式更新吗??带着这个疑问 ,我们 接下来进行逐...【详细内容】
2024-01-03  Search: vue3  点击:(36)  评论:(0)  加入收藏
React18 与 Vue3 全方面对比
1. 编程风格 & 视图风格1.1 编程风格 React 语法少、难度大;Vue 语法多,难度小例如指令:Vue<input v-model="username"/><ul> <li v-for="(item,index) in list" :key="inde...【详细内容】
2024-01-03  Search: vue3  点击:(72)  评论:(0)  加入收藏
Vue3 学习笔记,如何使用 Watch 监听数据变化
大家好,本篇文章我们继续学习和 Vue 相关的内容,今天我们归纳总结下如何使用 watch 监听组件中的数据变化,以及 computed 和 watch 的区别。什么是 watch,以及如何使用?watch 是...【详细内容】
2023-12-14  Search: vue3  点击:(163)  评论:(0)  加入收藏
Vue3 学习笔记,如何理解 Computed 计算属性
大家好,本篇文章我们继续学习和 Vue 相关的内容,今天我们归纳总结下什么是 computed 计算属性、如何使用和应用场景,以及 computed 和 Method 事件的区别和应用场景。什么是 co...【详细内容】
2023-12-11  Search: vue3  点击:(199)  评论:(0)  加入收藏
Vue3 学习笔记,如何定义事件以及如何理解响应式
如何定义事件在 Vue 中,可以使用 v-on 指令来绑定事件监听器。下面是一个示例,在点击按钮时触发事件处理程序:<template> <div> <button v-on:click="incrementCounter">{{...【详细内容】
2023-12-06  Search: vue3  点击:(136)  评论:(0)  加入收藏
最全Vue3开源管理系统汇总
搭建一个后台管理,从零开始开发,其实并不容易,在众多开源管理后台中,Vue3是一个备受瞩目的选择。它是一个现代化的前端框架,具有高效、灵活、易用等特点,今天,要为大家介绍几款开源...【详细内容】
2023-11-24  Search: vue3  点击:(201)  评论:(0)  加入收藏
Vue3问题:如何实现El-table内容超出省略提示?第三条很少有人会
一、需求分析,问题描述1、需求一个表格,分表头、表体、表尾三部分。当每个单元格的内容过长超出时,需要省略,用省略号代替超出的部分。同时,当鼠标移入上去时,会在上方弹出一个小...【详细内容】
2023-11-23  Search: vue3  点击:(90)  评论:(0)  加入收藏
Spring Boot + Vue3 前后端分离 实战wiki知识库系统
下栽の地止:https://www.itwangzi.cn/2508.html Spring Boot + Vue3 前后端分离 实战wiki知识库系统在当今的Web应用开发中,前后端分离已经成为了一种主流的开发模式。Spring...【详细内容】
2023-11-18  Search: vue3  点击:(142)  评论:(0)  加入收藏
深入详解Vue3中的Mock数据模拟
Vue3 前端开发时,需要api请求模拟和数据模拟,我将在接下来的时间写Mock模拟和json-server模拟。两种比较流行的模式,现在先介绍mock模式。使用NPM安装Mock.js库Mock.js是一个轻...【详细内容】
2023-11-07  Search: vue3  点击:(158)  评论:(0)  加入收藏
▌简易百科推荐
20k级别前端是怎么使用LocalStorage的,想知道吗?
当咱们把咱们想缓存的东西,存在localStorage、sessionStorage中,在开发过程中,确实有利于咱们的开发,咱们想看的时候也是一目了然,点击Application就可以看到。前言大家好,我是林...【详细内容】
2024-03-26  前端之神  微信公众号  Tags:前端   点击:(10)  评论:(0)  加入收藏
前端不存在了?盲测64%的人更喜欢GPT-4V的设计,杨笛一等团队新作
3 月 9 日央视的一档节目上,百度创始人、董事长兼 CEO 李彦宏指出,以后不会存在「程序员」这种职业了,因为只要会说话,人人都会具备程序员的能力。「未来的编程语言只会剩下两种...【详细内容】
2024-03-11  机器之心Pro    Tags:前端   点击:(9)  评论:(0)  加入收藏
前端开始“锈化”?Vue团队开源JS打包工具:基于Rust、速度极快、尤雨溪主导
Vue 团队已正式开源Rolldown &mdash;&mdash; 基于 Rust 的 JavaScrip 打包工具。Rolldown 是使用 Rust 开发的 Rollup 替代品,它提供与 Rollup 兼容的应用程序接口和插件接口...【详细内容】
2024-03-09  OSC开源社区    Tags:Vue   点击:(11)  评论:(0)  加入收藏
两年前端经验还不会手写Promise?
什么是promise?当我们处理异步操作时,我们经常需要进行一系列的操作,如请求数据、处理数据、渲染UI等。在过去,这些操作通常通过回调函数来处理,但是回调函数嵌套过多会导致代码...【详细内容】
2024-03-07  海燕技术栈  微信公众号  Tags:Promise   点击:(23)  评论:(0)  加入收藏
网站开发中的前端和后端开发有什么区别
前端开发和后端开发都是干什么的?有哪些区别?通俗地讲,前端干的工作是用户可以直接看得见的,而后端开发的工作主要在服务端,用户不太能直接看到。虽然前端开发和后端开发的工作有...【详细内容】
2024-02-21  CarryData    Tags:前端   点击:(31)  评论:(0)  加入收藏
网站程序开发中的前后端分离技术
随着互联网的快速发展和技术的不断创新,传统的网站开发模式已经难以满足日益增长的业务需求。为了提高开发效率、增强系统的可维护性和可扩展性,前后端分离技术逐渐成为了网站...【详细内容】
2024-01-31  网站建设派迪星航    Tags:前后端分离   点击:(23)  评论:(0)  加入收藏
如何优雅的实现前端国际化?
JavaScript 中每个常见问题都有许多成熟的解决方案。当然,国际化 (i18n) 也不例外,有很多成熟的 JavaScript i18n 库可供选择,下面就来分享一些热门的前端国际化库!i18nexti18ne...【详细内容】
2024-01-17  前端充电宝  微信公众号  Tags:前端   点击:(67)  评论:(0)  加入收藏
Vue中Scope是怎么做样式隔离的?
scope样式隔离在 Vue 中,样式隔离是通过 scoped 特性实现的。当在一个组件的 <style> 标签上添加 scoped 特性时,Vue 会自动为这个样式块中的所有选择器添加一个唯一的属性,以...【详细内容】
2024-01-04  海燕技术栈  微信公众号  Tags:Vue   点击:(80)  评论:(0)  加入收藏
vue3中 ref和 reactive的区别 ?
最近有朋友在面试过程中经常被问到这么一个问题,vue3 中的ref 和 reactive的区别在哪里,为什么 要定义两个API 一个 api不能实现 响应式更新吗??带着这个疑问 ,我们 接下来进行逐...【详细内容】
2024-01-03  互联网高级架构师  今日头条  Tags:vue3   点击:(36)  评论:(0)  加入收藏
React18 与 Vue3 全方面对比
1. 编程风格 & 视图风格1.1 编程风格 React 语法少、难度大;Vue 语法多,难度小例如指令:Vue<input v-model="username"/><ul> <li v-for="(item,index) in list" :key="inde...【详细内容】
2024-01-03  爱做梦的程序员  今日头条  Tags:Vue3   点击:(72)  评论:(0)  加入收藏
站内最新
站内热门
站内头条