赞
踩
ref
和reactive
是 Vue 3.0 中用于定义响应式数据的两个新 API。它们有以下区别:
ref
定义单个响应式数据
- 数据类型可以是任意类型。它通常用于定义原始数据类型为响应式数据。
- 返回一个响应式对象,该对象包含一个
.value
属性,可用于获取和设置数据。- 底层采用Object.defineProperty()实现
reactive
定义多个响应式数据
- 数据类型必须是对象
- 返回一个响应式对象,必须使用响应式对象来获取属性和设置数据
- 底层采用的是new Proxy()
reactive
函数可以用来创建一个响应式的对象,它会在内部使用Proxy
对对象的 getter 和 setter 进行拦截,从而实现对数据的依赖收集和通知更新。在vue3源码里,reactive的实现在core-main\packages\reactivity\src\reactive.ts文件中
reactive是一个函数,接收传入的target对象。返回一个createReactiveObject方法。
reactive的核心逻辑在createReactiveObject方法中。
- 首先校验传入的target必须是对象;
- 通过new Proxy创建一个代理对象,根据对象的类型走不同的handler处理逻辑;
- 返回代理对象proxy
手写一个简易的reactive,看下handler里是什么
- let handler = {
- //拦截整个对象,访问对象的属性时get拦截器触发
- get(target, key) {
- let value = target[key];
- if (typeof value === "object") {
- //如果访问的对象属性还是对象,进行递归
- return new Proxy(value, handler);
- }
- return value;
- },
- //拦截整个对象,当修改对象的属性的时候set拦截器会触发
- set(target, key, value) {
- target[key] = value;
- },
- };
- function reactive(target) {
- return new Proxy(target, handler);
- }
-
- let obj = { name: "jw", age: 30, n: [1, 2, 3, 4, 5] };
- //拿到obj的代理对象proxyObj
- const proxyObj = reactive(obj);
- //不访问obj,访问代理对象proxyObj
- console.log(proxyObj.name); //触发get拦截器
- proxyObj.age = 31; //触发set拦截器
- proxyObj.name = 100; //设置一个不存在的属性
handler
对象中,可以定义多个方法来控制代理对象的行为。在响应式reactive中,handler主要用了get和set两个方法。(是不是很熟悉?Object.defineProperty也有get和set方法,只不过是针对属性的;proxy这个是针对对象的,所以不一样哦)
get(target, property, receiver)
:拦截对象属性的读取操作,当访问代理对象的属性时会触发该方法。set(target, property, value, receiver)
:拦截对象属性的设置操作,当给代理对象的属性赋值时会触发该方法。这样每次你通过访问代理对象proxyObj访问的属性,都会被handler的get方法拦截,进而收集依赖。对proxyObj修改属性值的时候,被handler的set方法拦截,进行依赖更新。
注意:你不是操作原始对象obj,而是操作的代理对象proxyObj
在解析源码之前,先思考一个问题
你有没有想过,为什么使用ref定义的对象或基本类型,在js里写的时候都需要用.value啊。
这里不得不提到一个特殊的名称——typeScript类的属性访问器:get和set属性。
get 和 set 属性是一种在类中定义属性的方式,可以用来实现类的属性的读取和写入操作。基本语法如下:
- class MyClass {
- private _propertyName: any;
- get propertyName(): any {
- console.log("读取数据,自动进入get方法");
- return this._propertyName;
- }
- set propertyName(value: any) {
- this._propertyName = value;
- console.log("更新数据,自动进入set方法");
- }
- }
在上面的例子中,propertyName
是一个类的属性,它使用 get 和 set 属性定义在 MyClass
类中。在类的外部,可以使用点操作符来读取和写入 propertyName
属性,例如:
- const myObject = new MyClass();
- myObject.propertyName = 'hello';//更新
- console.log(myObject.propertyName); //访问
执行结果
当在类的外部读取 propertyName
属性时,会调用 propertyName
的 get 属性,返回 _propertyName
的值。当在类的外部写入 propertyName
属性时,会调用 propertyName
的 set 属性,将值赋给 _propertyName
属性。
这个是ts提供了一种写法,当我们在控制台使用tsc进行编译成.js文件后。
得到如下代码
- var MyClass = /** @class */ (function () {
- function MyClass() {
- }
- Object.defineProperty(MyClass.prototype, "propertyName", {
- get: function () {
- console.log("读取数据,自动进入get方法");
- return this._propertyName;
- },
- set: function (value) {
- this._propertyName = value;
- console.log("更新数据,自动进入set方法");
- },
- enumerable: false,
- configurable: true
- });
- return MyClass;
- }());
- var myObject = new MyClass();
- myObject.propertyName = "hello";
- console.log(myObject.propertyName);
可以看到,最终propertyName编译成一个属性,并且通过Object.defineProperty进行了get和set方法的重写。
OK,先说到这,带着这个理解去看ref的源码
在vue3源码里,ref的实现也是在reactivity文件夹下:core-main\packages\reactivity\src\ref.ts
ref是一个函数,接收一个unknown类型的参数value;
返回一个createRef方法,第一个参数是value,第二个参数是false.
createRef最终返回一个RefImpl实例对象。在RefImp类里,首先通过构造函数,创建一个_value对象。toReactive根据value类型,如果是对象的话,用reactive方法处理对象。
RefImpl类还定义了get和set方法,在类里使用"get空格value"这种方式很少见。这其实是ts类的属性访问器写法,当使用
.value
属性来获取或设置数据时,会自动调用get
或set
访问器函数。在底层,这种写法会被编译成
Object.defineProperty
,这是 JavaScript 中用于定义对象属性的原生方法。使用Object.defineProperty
可以定义对象的属性的特性,如可枚举性、可配置性、可写性等,并可以为属性设置get
和set
函数,从而实现属性的访问器功能。(前面已经介绍过原理了)
toReactive就是判断ref传入的是不是对象,如果是对象,去调用reactive方法将对象变成响应式的。ref这个老六,就是万精油,啥都能处理。
在Vue3中,无论是基本类型还是复杂类型的响应式数据,都推荐使用
ref
来创建。这样做的好处是,你可以统一使用.value
属性来访问和修改响应式数据的值,而不需要在基本类型和复杂类型之间切换使用方式。
简单性:ref
提供了一种简单的方式来定义响应式对象,只需传入初始值即可。相比之下,reactive
需要将整个对象传入,稍显繁琐。对于单个变量或简单数据,使用 ref
更加直观和方便。
透明性:ref
返回的是一个包装过的对象,可以通过 .value
访问其值,这种包装使得数据访问更加明确和直观。而 reactive
返回的是原始对象,需要通过代理访问属性,有时会增加代码的复杂性。(复杂的代码谁想写啊)
性能:在某些情况下,ref
比 reactive
更高效。因为 ref
包装的是基本类型数据,而 reactive
包装的是对象,对于简单数据类型,ref
的性能可能更好。
推荐度:Vue 3 官方文档和社区更倾向于推荐使用 ref
,因为它更简单、更直观,适用于大多数场景。而 reactive
更适合处理复杂的对象或数据结构。
尽管程序员通常建议使用
ref
,但在实际开发中,根据具体情况选择合适的方式是更为重要的。对于简单的数据,使用ref
可能更加方便和直观;而对于复杂的对象或数据结构,使用reactive
可能更合适。
看到这的,给我来波666,太烧脑了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。