为什么在Vue3中有了Reactive还需要Ref ?

在Vue 中使用选项式API ,去声明响应式对象是很简单的。我们只需要将需要响应式的变量属性放进 data 函数中,并return 出来。 Vue 框架会帮我们把数据变为响应式,并在模板中可用。 如下面所示。

javascript 复制代码
export defaut {
	// other 
	   data() {
         return {
             hello: 'world'
         }
     }	
	//... other code
}

但对于 Composition API,事情就没那么简单了。状态声明必须使用Ref 和 Reactive 函数来实现,这两个函数的行为表现也不一样。

让我们讨论一下 Vue 3 中发生了什么变化以及为什么我们需要两个不同的助手。

Vue3 中的响应式实现

大家都知道Vue2 中,是通过Object.defineProperty 实现的依赖收集和响应式处理。但在Vue3 使用了javascript 的Proxy API 完全重写了响应式核心。

Proxy 是一种更现代更优雅的API 可以实现对一个对象的代理和劫持。

你可以通过下边一段代码来了解Proxy 是如何工作的:

vbnet 复制代码
const userInfo = {
  name: "sean",
  age: 35,
};

const handler = {
  get(target, property) {
    if (property === "name") {
      const name = target[property]
      return name.charAt(0).toUpperCase() + name.slice(1);
    }
    if (property === "age") {
      return '--'
    }
    return target[property]
  },
};

const proxyObj = new Proxy(userInfo, handler);

console.log(proxyObj.name) // "Sotis"
console.log(proxyObj.age) // "--"

handler 内的 get 方法称为 trap ,每次访问对象的属性时都会调用该方法。

同样的道理,我们很容易就能推断出一个 set 的 trap 方法。

javascript 复制代码
const userInfo = {
  name: "Sean",
  age: 35,
};

const handler = {
  set(target, prop, value) {
    if (prop === "age") {
      if (!Number.isInteger(value)) {
        throw new TypeError("age 类型错误");
      }
      if (value > 200) {
        throw new RangeError("额,好像超过了范围了");
      }
    }
    target[prop] = value;
    return true;
  },
};

const proxy = new Proxy(userInfo, handler);

proxy.age = 12 // OK
proxy.age = 300 // Error: 额,好像超过了范围了

这就是Vue3 的响应式实现的核心原理,当我们使用Reactive 工具方法声明响应式属性的时候, 框架底层会通过Proxy 来实现,属性的变更追踪和依赖处理。

scss 复制代码
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, value) {
      target[key] = value
      trigger(target, key)
    }
  })
}

当然,Vue 框架中的响应式,实现要比这复杂的多。 框架需要处理很多边界情况。 但是核心的原理还是使用了Proxy。 如果你对Vue 的Reactive的具体实现感兴趣,请看如下连接。

github.com/vuejs/core/...

上面的代码片段解释了为什么将响应式变量解构或重新分配给局部变量不再具有响应式的特性,因为它不再触发源对象上的 get/set 代理track。

这看起来是一个让一切变得响应式的完美解决方案。但是有一个问题!根据定义,Proxy 仅适用于复杂类型(对象、数组、映射和集合)。

想要将原始值( Boolean、Number、BigInt、String、Symbol、undefined 和 null 等类型的值) 变成响应式数据,就必须对其做一层包裹,也就是我们接下来要介绍的 ref。

csharp 复制代码
function ref(value) {
  const refObject = {
    get value() {
      track(refObject, 'value')
      return value
    },
    set value(newValue) {
      value = newValue
      trigger(refObject, 'value')
    }
  }
  return refObject
}

通过将原始值,进行包装成Object 我们实现了, 原始值的响应式。

这也就解释了为什么必须在脚本设置中使用的烦人的 .value。

同样,重组或重新分配给局部变量是行不通的。

总结

所以为什么同时需要 Ref 和 Reactive 的答案是:

  • 代理。对于复杂类型,可以直接使用它们,但对于原始类型数据需要创建代理对象。

希望了解 Vue 的底层工作原理可以让您更加高效,并消除 ref 和 Reactive 之间的混淆。

相关推荐
少年姜太公5 小时前
什么?还不知道git cherry pick?
前端·javascript·git
白兰地空瓶7 小时前
🏒 前端 AI 应用实战:用 Vue3 + Coze,把宠物一键变成冰球运动员!
前端·vue.js·coze
Liu.7748 小时前
vue3使用vue3-print-nb打印
前端·javascript·vue.js
松涛和鸣9 小时前
Linux Makefile : From Basic Syntax to Multi-File Project Compilation
linux·运维·服务器·前端·windows·哈希算法
dly_blog9 小时前
Vue 逻辑复用的多种方案对比!
前端·javascript·vue.js
万少9 小时前
HarmonyOS6 接入分享,原来也是三分钟的事情
前端·harmonyos
烛阴9 小时前
C# 正则表达式:量词与锚点——从“.*”到精确匹配
前端·正则表达式·c#
wyzqhhhh10 小时前
京东啊啊啊啊啊
开发语言·前端·javascript
JIngJaneIL10 小时前
基于java+ vue助农电商系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
q_191328469510 小时前
基于Springboot+MySQL+RuoYi的会议室预约管理系统
java·vue.js·spring boot·后端·mysql·若依·计算机毕业设计