vue3之ref、reactive、toRef和toRefs的用法和区别、一阵见血的见解、使用上的区别、本质区别、响应式、坑、elementPlus、select


ref

概述

生成值类型响应式数据
通过.value值修改
生成对象和数组类型的响应式对象选用reactive方式比较好


html

<template>
	<div>
		<div>countRef: {
   
   { countRef }}</div>
		<div>objCountRef: {
   
   { objCountRef.count }}</div>
		<div>爱好: {
   
   { hobbyRef.join('---') }}</div>
	</div>
</template>

JavaScript

import {
    
     ref } from 'vue';

export default {
    
    
	name: 'refDemo',
	setup () {
    
    
		// 值类型
		const countRef = ref(1);
		console.log(countRef);
		
		// 对象
		const objCountRef = ref({
    
     count: 1 });
		
		// 数组
		const hobbyRef = ref(['爬山', '游泳']);
		
		setTimeout(() => {
    
    
			// 通过value改变值
			countRef.value = 2;
			objCountRef.value.count = 3;
			hobbyRef.value.push('吃饭');
		}, 3000);
		
		return {
    
    
			countRef,
			objCountRef,
			hobbyRef
		};
	}
}

reactive

概述

reactive方法根据传入的对象,创建返回一个深度响应式对象。响应式对象看起来和传入的对象一样。但是,响应式对象属性值改动,不管层级有多深,都会触发响应式,新增和删除属性也会触发响应式。
1、改变name属性
2、深度改变address属性
3、新增school属性
4、删除age属性


html

<template>
	<div class="demo">
		<div>姓名: {
   
   { state.name }}</div>
		<div v-if="state.age > 0">年龄: {
   
   { state.age }}</div>
		<div>地址: {
   
   { state.address.provoince }} - {
   
   { state.address.city }} - {
   
   { state.address.street }}</div>
	</div>
	
	<div class="demo">
		<div>学校: {
   
   { state.school || '自学成才' }}</div>
	</div>
</template>

JavaScript

import {
    
     reactive } from 'vue';

export default {
    
    
	name: 'reactiveDemo',
	setup () {
    
    
		// 响应式对象
		const state = reactive({
    
    
			name: '太凉',
			age: 18,
			hobby: ['游泳', '爬山'],
			address: {
    
    
				provoince: '北京',
				city: '北京',
				street: '东城区长安街'
			}
		});
		
		// 过3秒后改变
		setTimeout(() => {
    
    
			// update1: 改变name属性
			state.name = '冰箱太凉';
			state.age = 25;
			// update2: 深度改变
			state.address.provoince = '山东省';
			// address属性
			state.address.city = '临沂市';
			// update3: 新增school属性
			state.school = '清华北大';
			// update4: 删除年龄属性
			delete state.age;
			// update5: 数组添加一项
			state.hobby.push('打豆豆');
		}, 3000);
		
		return {
    
    
			// 注意这里不能通过...state方式结构,
			// 这样会丢失响应式
			state  
		};
	}
}

style

.demo {
    
    
	text-align: left;
	width: 600px;
	margin: 20px auto;
}

toRef

概述

1、针对一个响应式对象(reactive封装)的prop(属性)创建一个ref,且保持响应式
2、两者保持引用关系


html

<template>
	<div class="demo">
		<div>姓名--state.name: {
   
   { state.name }}</div>
		<div>姓名2--nameRef: {
   
   { nameRef }}</div>
		<div>年龄: {
   
   { state.age }}</div>
	</div>
</template>

JavaScript

import {
    
     reactive, toRef } from 'vue';

export default {
    
    
	name: 'toRefDemo',
	setup () {
    
    
		// 响应式对象
		const state = reactive({
    
    
			name: '太凉',
			age: 18
		});
		
		// 通过toRef创建一个Ref响应式
		const nameRef = toRef(state, 'name');
		
		// 过3秒后改变 两者 保持引用关系 
		setTimeout(() => {
    
    
			// update1: 改变name属性
			state.name = '冰箱太凉';
		}, 3000);
			
		// 过6秒后改变两者保持引用关系 
		setTimeout(() => {
    
    
			// update1: 改变name属性
			nameRef.value = '我就是冰箱太凉';
		}, 6000);
		
		return {
    
    
			nameRef,
			state
		};
	}
}

toRefs

概述

toRefs是一种用于破坏响应式对象并将其所有属性转换为ref的实用方法
1、将响应式对象(reactive封装)转成普通对象
2、对象的每个属性(prop)都是对应的ref
3、两者保持引用关系


html

<template>
	<div class="demo">
		<h3>state方式不推荐的方式绑定</h3>
		<div>姓名--state.name: {
   
   { state.name }}</div>
		<div>年龄--state.age: {
   
   { state.age }}</div>
	</div>
	
	<div class="demo">
		<h3>toRefs之后的方式推荐这种方式,return需要{ ...toRefs(state) }</h3>
		<div>姓名--name: {
   
   { name }}</div>
		<div>年龄--age: {
   
   { age }}</div>
	</div>
</template>

JavaScript

import {
    
     reactive, toRefs } from 'vue';

export default {
    
    
	name: 'toRefsDemo',
	setup () {
    
    
		// 响应式对象
		const state = reactive({
    
    
			name: '太凉',
			age: 18
		});
		
		// 通过toRefs创建一个响应式对象属性的Ref
		const toRefsValue = toRefs(state);
		
		// 过3秒后改变  两者保持引用关系
		setTimeout(() => {
    
    
			state.name = '冰箱太凉';
			state.age = '30';
		}, 3000);
		
		// 过6秒后改变 两者保持引用关系
		setTimeout(() => {
    
    
			toRefsValue.name.value = '我就是宇宙小超人';
			toRefsValue.age.value = '101';
		}, 6000);
		
		return {
    
    
			// 不建议使用这种方式,可以用下面的方式直接替换
			state,
			// 最佳方式:这里是结构将name的ref,
			// age的ref结构到对象根下面
			...toRefsValue
		};
	}
}

使用toRefs(state)方式返回

<template>
	<div>
		<div>姓名:{
   
   { name }}</div>
		<div>年龄:{
   
   { age }}</div>
	</div>
</template>
import {
    
     reactive, toRefs } from 'vue';

export default {
    
    
	setup () {
    
    
		const state = reactive({
    
    
			age: 20,
			name: '太凉'
		});
		
		const stateAsRefs = toRefs(state);
		
		return {
    
    
			...stateAsRefs
		};
	}
}

ref和reactive使用上区别

reactive定义引用数据类型,ref定义基本类型
reactive定义的变量直接使用,ref定义的变量使用时需要.value
模板中均可直接使用,vue帮我们判断了是reactive还是ref定义的(通过__v_isRef属性),从而自动添加了.value


ref和reactive的本质区别

代码片段壹

源码细节较多,本部分只分析关于refreactive的核心代码
首先找到ref函数,调用了createRef函数,而createRef返回了RefImpl

export function ref(value?: unknown) {
     
     
	// 创建ref
	return createRef(value, false);
}

function createRef(rawValue: unknown, shallow: boolean) {
     
     
	if (isRef(rawValue)) return rawValue;
	
	// 返回一个RefImpl对象
	return new RefImpl(rawValue, shallow);
}

代码片段贰

RefImpl类是不是似曾相识的感觉,没错就是Object.defineProperty,这里做的事情就是收集依赖,触发依赖,只是换了个写法而已。

class RefImpl<T> {
     
     
	// 用来保存加工后实现响应化的值
	private _value: T
	// 用来保存当前未经加工过的值
	private _rawValue: T
	// 用来收集依赖,这是一个Set类型
	public dep?: Dep = undefined;
	// 用来标识该值是否经过ref加工
	public readonly __v_isRef = true;
	
	constructor(value: T, public readonly __v_isShallow: boolean) {
     
     
		// __v_isShallow默认没有传,
		// 故默认为undefined,这里分别调用toRaw和toReactive
		this._rawValue = __v_isShallow ? value : toRaw(value);
		this._value = __v_isShallow ? value : toReactive(value);
	}
	
	get value() {
     
     
		trackRefValue(this);
		
		return this._value;
	}
	
	set value(newVal) {
     
     
		const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal);
		newVal = useDirectValue ? newVal : toRaw(newVal);
		if (hasChanged(newVal, this._rawValue)) {
     
     
			this._rawValue = newVal;
			this._value = useDirectValue ? newVal : toReactive(newVal);
			triggerRefValue(this, newVal);
		}
	}
}

代码片段叁

toReactive函数如果传入的参数是一个对象的话,返回值将会调用reactive方法来进行包裹,reactive最终会通过Proxy来实现响应式。

export const toReactive = <T extends unknown>(value: T): T => isObject(value) ? reactive(value) : value;

总结

1、基础类型值(StringNumberBooleanSymbol)或单值对象(类似{ count: 1 }这样只有一个属性值的对象)使用ref
2、引用类型值(ObjectArray)使用reactive


reactive将引用类型值变为响应式,使用Proxy实现
ref可将基本类型和引用类型都变成响应式,通过监听类的value属性的getset实现,但是当传入的值为引用类型时实际上内部还是使用reactive方法进行的处理
ref经修改实现方式后性能更高,推荐使用ref一把梭


1、ref创建基本数据类型时,是通过vue2中类似与Object.defineProperty的做法实现响应
2、ref创建对象时,内部调用的是reactive方法
3、vue3ref实际是对reactive的二次封装增强,reactive能做的ref一定能做,所以开发中多多使用ref


一阵见血的见解

1、ref把数据封装成一个整体,重新给数据指向新的内存地址,并且让其拥有响应式功能。
2、reactive把引用数据的每一个数据打散成单个独立的响应式数据,以便对数据的增删改查操作,也就是深度监听的意思。
3、开发中如果想重置一个数组那么建议使用ref,因为ref重置了内存地址;如果是一个固定数据对象,只是想修改数据中的某个属性值,那么使用reactive比较合适。


在对refreactive不是很了解的时候误用reactive定义一个空数组用来存放后端返回的数据,结果请求回来的数据一直无法正常的在页面上显示。
在使用vue3+typeScript+elementPlus+select实现下拉框选择时,下拉框的数据一直是’无数据’状态,后来使用ref解决了此问题。

猜你喜欢

转载自blog.csdn.net/weixin_51157081/article/details/132287166