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的本质区别
代码片段壹
源码细节较多,本部分只分析关于
ref
和reactive
的核心代码
首先找到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、基础类型值(
String
、Number
、Boolean
或Symbol
)或单值对象(类似{ count: 1 }
这样只有一个属性值的对象)使用ref
2、引用类型值(Object
、Array
)使用reactive
贰
reactive
将引用类型值变为响应式,使用Proxy
实现ref
可将基本类型和引用类型都变成响应式,通过监听类的value
属性的get
和set
实现,但是当传入的值为引用类型时实际上内部还是使用reactive
方法进行的处理ref
经修改实现方式后性能更高,推荐使用ref
一把梭
叁
1、
ref
创建基本数据类型时,是通过vue2
中类似与Object.defineProperty
的做法实现响应
2、ref
创建对象时,内部调用的是reactive
方法
3、vue3
中ref
实际是对reactive
的二次封装增强,reactive
能做的ref
一定能做,所以开发中多多使用ref
一阵见血的见解
1、
ref
把数据封装成一个整体,重新给数据指向新的内存地址,并且让其拥有响应式功能。
2、reactive
把引用数据的每一个数据打散成单个独立的响应式数据,以便对数据的增删改查操作,也就是深度监听的意思。
3、开发中如果想重置一个数组那么建议使用ref
,因为ref
重置了内存地址;如果是一个固定数据对象,只是想修改数据中的某个属性值,那么使用reactive
比较合适。文章来源:https://www.toymoban.com/news/detail-604284.html
坑
在对
ref
和reactive
不是很了解的时候误用reactive
定义一个空数组用来存放后端返回的数据,结果请求回来的数据一直无法正常的在页面上显示。
在使用vue3
+typeScript
+elementPlus
+select
实现下拉框选择时,下拉框的数据一直是’无数据’状态,后来使用ref
解决了此问题。文章来源地址https://www.toymoban.com/news/detail-604284.html
到了这里,关于java学习路程之篇三、进阶知识、面向对象高级、接口新特性、代码块、内部类、Lambda表达式、窗体、组件、事件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!