目录
一、组合API
setup函数是组合API的入口函数。
setup函数执行时机是在beforeCreate之前。
在setup函数中,不能使用data和methods,为了避免出现错误,setup中的this为undefined。
setup函数是同步的。
注意点:
1.在组合API中定义的变量/方法,要想在外界使用的话,必须通过 return {xxx,xxx} 暴露出来。
2.在组合API中,定义的方法不用定义到methods中,直接定义即可。
二、reactive函数
1.什么是reactive?
reactive是vue3中提供的实现响应式数据的方法。在vue2中的响应式数据使用defineProperty来实现的。在vue3中实现响应式数据使用ES6中的proxy方法来实现。
注意点:
- reactive的参数必须是对象的形式(json/arr)。
- 如果给reactive传递了其他对象,默认情况下修改对象,界面不会更新,若想更新,则通过赋值的方式 。
<template>
<div>
<p>{{ user }}</p>
<button @click="increase">click me! one year later</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "reactive",
//创建一个响应式数据
//本质:就是将传入的数据包装成proxy对象
setup() {
const user = reactive({ name: "Alice", age: 12 });
function increase() {
++user.age
}
return { user, increase };
},
};
</script>
三、ref函数
1.什么是ref函数?
ref和reactive一样,也是用来实现响应式数据的方法。由于reactive函数你需传递一个对象,所以导致在企业开发中,只是实现单个对象的时候会非常的麻烦。所以vue3提供了一个ref方法,实现对简单值的监听。
2.ref的本质
ref底层的本质其实还是reactive,系统会根据我们传入的ref的值将它转化为reactive
ref(xxx)------->> reactive({ value:xxx })
3.ref的注意点:
在vue中使用的ref的值不用通过value来获取。
在js中ref的值必须要通过value来获取。
四、ref和reactive的区别:
如果在template中,使用的是ref类型是数据,那么vue会自动帮我们添加.value
如果在template中,使用的是reactive类型是数据,那么vue不会自动帮我们添加.value
vue如何决定是否需要自动添加ref类型的?
vue在解析数据之前,会自动判断ref类型,如果是就添加.value,反之则否。
vue是如何判断当前数据是否是ref类型的?
通过当前数据的_ _v_ref来判断的,如果有这个私有的属性,并且取值为true,那么代表是一个ref类型的数据。
我们也可以通过isRef和isReactive的值的true和false来判断当前的数据的类型。
五、 递归监听和非递归监听
递归监听
默认情况下,无论是ref还是reactive都是递归监听,即可以实时监听到数据的变化。
注意点:递归监听存在很大的问题,如果数据量比较大的话,非常消耗性能。
在reactive中:
<template>
<div>
<p>msg.a.b.c = {{msg.a.b.c}}</p>
<p>msg.e.f = {{msg.e.f}}</p>
<p>msg.g = {{msg.g}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive({
a: {
b: {
c: 'c'
}
},
e: {
f: 'f'
},
g: 'g'
});
function c() {
console.log(msg);
msg.a.b.c = 'C';
msg.e.f = 'F';
msg.g = 'G';
};
return {
msg,
c
};
}
}
</script>
在ref中:
<template>
<div>
<p>msg.a.b.c = {{msg.a.b.c}}</p>
<p>msg.e.f = {{msg.e.f}}</p>
<p>msg.g = {{msg.g}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
let msg = ref({
a: {
b: {
c: 'c'
}
},
e: {
f: 'f'
},
g: 'g'
});
function c() {
console.log(msg);
msg.value.a.b.c = 'C';
msg.value.e.f = 'F';
msg.value.g = 'G';
};
return {
msg,
c
};
}
}
</script>
非递归监听
shallowRef / shallowReactive
如果通过shallowRef创建数据,监听的是.value的变化,而不是第一层的变化 。
如何触发非递归监听来更新页面?
如果是shallowRef类型数据,则通过triggerRef来触发。
在 shallowReactive
中,并没有提供 trigger
方案来主动唤醒监测变化。
(1)shallowRef的本质:通过shallowRef创建的数据,监听的是.value的变化,因为底层本质上value才是第一层。
<template>
<div></div>
</template>
<script setup>
import { reactive } from 'vue'
import { shallowReactive } from 'vue'
import { shallowRef } from 'vue'
export default {
setup() {
// ref -> reactive
// ref(10) -> reactive({value:10})
// shallowRef -> shallowReactive
// shallowRef(10) -> shallowReactive({value:10})
let state1 = shallowRef({
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
})
let state2 = shallowReactive({
value: {
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
}
})
}
}
</script>
应用场景
一般情况下,我们监听数据变化使用ref和reactive即可
如果数据量比较大的情况下,我们才使用shallowReactive和shallowRef来监听数据变化。
六、toRaw
ref/reactive数据类型的特点:每次修改都会被追踪,更新ui界面,非常消耗性能
如果不需要更新ui界面,不会被追踪数据,可以使用toRaw拿到原始数据,对原始数据进行修改。这样可以提高性能。
注意:当toRaw
方法接收的参数是ref
对象时,需要加上.value
才能获取到原始数据对象。
reactive中的toRaw
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.toRaw
从Reactive 或 Ref中得到原始数据
2.toRaw作用
做一些不想被监听的事情(提升性能)
* */
import {reactive, toRaw} from 'vue';
export default {
name: 'App',
setup() {
/*
ref/reactive数据类型的特点:
每次修改都会被追踪, 都会更新UI界面, 但是这样其实是非常消耗性能的
所以如果我们有一些操作不需要追踪, 不需要更新UI界面, 那么这个时候,
我们就可以通过toRaw方法拿到它的原始数据, 对原始数据进行修改
这样就不会被追踪, 这样就不会更新UI界面, 这样性能就好了
* */
let state = reactive(
{name:'lnj', age:18}
);
// 获取state的源数据
let obj2 = toRaw(state);
// console.log({name:'lnj', age:18} === obj2); // true
// console.log({name:'lnj', age:18} === state); // false
function myFn() {
// 获取的源数据更改,不会触发页面更新
obj2.name = 'zs';
console.log(obj2); // {name: "zs", age: 18}
// state.name = 'zs';
// console.log(state);// {name: "zs", age: 18}
}
return {state, myFn}
}
}
</script>
<style>
</style>
ref中的toRaw
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
import { toRaw, ref} from 'vue';
export default {
name: 'App',
setup() {
/*
1.ref本质: reactive
ref(obj) -> reactive({value: obj})
* */
let state = ref({name:'lnj', age:18});
// 注意点: 如果想通过toRaw拿到ref类型的原始数据(创建时传入的那个数据)
// 那么就必须明确的告诉toRaw方法, 要获取的是.value的值
// 因为经过Vue处理之后, .value中保存的才是当初创建时传入的那个原始数据
// let obj2 = toRaw(state);
let obj2 = toRaw(state.value);
console.log(state);
console.log(obj2);
function myFn() {
// 获取的源数据更改,不会触发页面更新
obj2.name = 'zs';
console.log(obj2); // {name: "zs", age: 18}
// state.name = 'zs';
// console.log(state);// {name: "zs", age: 18}
}
return {state, myFn}
}
}
</script>
<style>
</style>
七、markRaw
将数据标记为永远不能追踪的数据
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.markRaw
将数据标记为永远不能追踪的数据
一般在编写自己的第三方库时使用
* */
import {reactive, markRaw} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name: 'lnj', age: 18};
// 不能追踪,监听,作为响应式的数据
obj = markRaw(obj);
let state = reactive(obj);
function myFn() {
// 数据更改了,但是页面ui还是没有发生改变
state.name = 'zs';
}
return {state, myFn}
}
}
</script>
<style>
</style>
八、ref、toRef、toRefs
1、ref和toRef的区别
(1). ref本质是拷贝,修改响应式数据不会影响原始数据;toRef的本质是引用关系,修改响应式数据会影响原始数据
(2). ref数据发生改变,界面会自动更新;toRef当数据发生改变是,界面不会自动更新
(3). toRef传参与ref不同;toRef接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性
2、toRef和toRefs区别:
(1)toRef是创建一个ref数据,和原始数据相关联
(2)toRefs是批量创建ref数据,和原始数据相关联
ref代码:
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
import {ref} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name:'lnj'};
let state = ref(obj.name);
function myFn() {
state.value = 'zs';
console.log(obj); //{name:'lnj'}
console.log(state); // {name:'zs'}
}
return {state, myFn}
}
}
</script>
<style>
</style>
toRef代码:
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.toRef
创建一个ref类型数据, 并和以前的数据关联
2.toRefs
批量创建ref类型数据, 并和以前数据关联
3.toRef和ref区别
ref-创建出来的数据和以前无关(复制)
toRef-创建出来的数据和以前的有关(引用)
ref-数据变化会自动更新界面
toRef-数据变化不会自动更新界面
* */
import {ref, toRef} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name:'lnj'};
/*
ref(obj.name) -> ref(lnj)
-> reactive({value:lnj})
* */
// ref->复制
// let state = ref(obj.name);
// toRef->引用
/*
ref和toRef区别:
ref->复制, 修改响应式数据不会影响以前的数据
toRef->引用, 修改响应式数据会影响以前的数据
ref->数据发生改变, 界面就会自动更新
toRef->数据发生改变, 界面也不会自动更新
toRef应用场景:
如果想让响应式数据和以前的数据关联起来, 并且更新响应式数据之后还不想更新UI, 那么就可以使用toRef
* */
let state = toRef(obj, 'name');
function myFn() {
state.value = 'zs';
/*
结论: 如果利用ref将某一个对象中的属性变成响应式的数据
我们修改响应式的数据是不会影响到原始数据的obj !== state
* */
/*
结论: 如果利用toRef将某一个对象中的属性变成响应式的数据
我们修改响应式的数据是会影响到原始数据的
但是如果响应式的数据是通过toRef创建的, 那么修改了数据并不会触发UI界面的更新
* */
console.log(obj); //{name:'zs'}
console.log(state); //{name:'zs'}
}
return {state, myFn}
}
}
</script>
<style>
</style>
toRefs代码:
<template>
<div>
<p>{{state}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.toRef
创建一个ref类型数据, 并和以前的数据关联
2.toRefs
批量创建ref类型数据, 并和以前数据关联
3.toRef和ref区别
ref-创建出来的数据和以前无关(复制)
toRef-创建出来的数据和以前的有关(引用)
ref-数据变化会自动更新界面
toRef-数据变化不会自动更新界面
* */
import {ref, toRef, toRefs} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name:'lnj', age:18};
// let name = toRef(obj, 'name');
// let age = toRef(obj, 'age');
let state = toRefs(obj);
function myFn() {
// name.value = 'zs';
// age.value = 666;
state.name.value = 'zs';
state.age.value = 666;
console.log(obj); //{name:'zs', age:666}
console.log(state); //{name:'zs', age:666}
// console.log(name);
// console.log(age);
}
return {state, myFn}
}
}
</script>
<style>
</style>
九、customRef
customRef:返回一个对象,可以显式地控制依赖追踪和响应触发
<template>
<div>
<p>{{obj}}</p>
<button @click="lnc">button</button>
</div>
</template>
<script>
import { customRef,ref } from 'vue';
// customRef用于 自定义ref
// 自定义 ref 需要提供参数传值
function myRef(value) {
// 自定义 ref 需要提供 customerRef 返回值
// customer 需要提供一个函数作为参数
// 该函数默认带参数 track 和 trigger ,都是方法。
return customRef((track, trigger) => {
return {
// customer 需要提供一个对象 作为返回值
// 该对象需要包含 get 和 set 方法。
get() {
// track 方法放在 get 中,用于提示这个数据是需要追踪变化的
track();
console.log('get', value);
return value;
},
// set 传入一个值作为新值,通常用于取代 value
set(newValue) {
console.log('set', newValue);
value = newValue;
// 记得触发事件 trigger,告诉vue触发页面更新
trigger();
}
}
})
}
export default {
name: 'App',
setup() {
// let obj = ref(18); // reactive({value: 18})
// 应用上面的自定义 ref ,使用方案和之前的 ref 是类似的。
const obj = myRef(123);
function lnc() {
obj.value += 1;
}
return {
obj,
lnc
};
}
}
</script>
十、readonly 、shallowReadonly、isReadonly
readonly 用于创建一个只读的数据,并且是递归只读。层级深的数据也是没有变化。而且页面并没有更新 。
shallowReadonly用于创建一个只读的数据,但是不是递归只读。
isReadonly用于判断是否是只读数据。
<template>
<div>
<p>{{state.name}}</p>
<p>{{state.attr.age}}</p>
<p>{{state.attr.height}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
/*
1.readonly
- 只读数据
2.readonly和const
- const 赋值保护
- readonly 递归保护
3.isReadonly
- 判断是否是readonly
4.shallowReadonly
- 非递归保护
* */
import {readonly, isReadonly, shallowReadonly} from 'vue'
export default {
name: 'App',
setup() {
// readonly:用于创建一个只读的数据, 并且是递归只读
let state = readonly({name:'lnj', attr:{age:18, height: 1.88}});
// shallowReadonly: 用于创建一个只读的数据, 但是不是递归只读的
// let state = shallowReadonly({name:'lnj', attr:{age:18, height: 1.88}});
// const和readonly区别:
// const: 赋值保护, 不能给变量重新赋值,
// readonly: 属性保护, 不能给属性重新赋值
// const value = 123;
const value = {name:'zs', age:123};
function myFn() {
state.name = '知播渔';
state.attr.age = 666;
state.attr.height = 1.66;
console.log(state);
console.log(isReadonly(state)); //true
// value = 456;
// console.log(value);
value.name = 'ls';
value.age = 456;
console.log(value);
}
return {state, myFn};
}
}
</script>
<style>
</style>
十一、vue3响应式数据的本质
- 在Vue2.x中是通过defineProperty来实现响应式数据的
- 在Vue3.x中是通过Proxy来实现响应式数据的,Proxy除了可以监听对象外还可以监听数组