为什么在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 之间的混淆。

相关推荐
小阮的学习笔记1 小时前
electron实现加载页(启动页)
vue.js·electron
小着2 小时前
vue项目页面最底部出现乱码
前端·javascript·vue.js·前端框架
lichenyang4535 小时前
React ajax中的跨域以及代理服务器
前端·react.js·ajax
呆呆的小草5 小时前
Cesium距离测量、角度测量、面积测量
开发语言·前端·javascript
一 乐6 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
testleaf6 小时前
前端面经整理【1】
前端·面试
BillKu6 小时前
Vue3 + TypeScript + Element Plus 表格行按钮不触发 row-click 事件、不触发勾选行,只执行按钮的 click 事件
vue.js·elementui·typescript
好了来看下一题6 小时前
使用 React+Vite+Electron 搭建桌面应用
前端·react.js·electron
啃火龙果的兔子6 小时前
前端八股文-react篇
前端·react.js·前端框架
小前端大牛马6 小时前
react中hook和高阶组件的选型
前端·javascript·vue.js