vue3的相关api
# 响应式系统工具集
# ref
接收一个参数值并返回一个响应式且可改变的ref对象。
ref对象拥有一个指向内部值的一个属性.value
当ref在模板中使用的时候,他会自动解套,无需在模板内部额外写.value(意思是return出来以后,用小胡子就可以直接使用)
ref不是Proxy实现的。
# unref
- 如果参数是一个ref,则返回它的value值,否则返回参数本身,他是val = isRef(val)? val.value:val的语法糖
let a = '你好',
b = ref(2)
console.log(unref(a,b)) //'你好',2
1
2
3
2
3
# toRef
- 将参数转化为ref对象,例如
let a = 0;
console.log(toRef(a)) //ObjectRefImpl {_object: 0, _key: undefined, __v_isRef: true}
1
2
2
# toRefs
- 把响应式对象转为普通对象,该普通对象每一个property都是一个ref,和响应式对象property一一对应。
# isRef
- 判断该值是否为ref对象,如果是则返回true,否则是false
let a = 0
console.log(isRef(a)) // false
1
2
2
# isProxy
- 检查一个对象是否由reactive或者readonly方法创建的代理
let a = 0
console.log(isRef(a)) // false
1
2
2
# isReactive
- 检查一个对象是否由reactive创建的响应式代理。
- 如果这个代理是由reactive创建的,但是又被reactive创建的另一个代理包裹了一层,那么同样会返回true
let a = 0
console.log(isRef(a)) // false
1
2
2
# readonly
- 传入一个对象(响应式或者普通)或ref,返回一个原始对象的只读代理,一个只读的代理是深层的,对象内部任何嵌套的属性也只是只读的。
# isReaonly
- 检查一个对象是否由readonly创建的只读代理。。
- 如果这个代理是由reactive创建的,但是又被reactive创建的另一个代理包裹了一层,那么同样会返回true
let a = 0
console.log(isRef(a)) // false
1
2
2
# customRef
- customRef 用于自定义返回一个ref对象,可以显式地控制依赖追踪和触发响应,接受工厂函数
- 两个参数分别是用于追踪的 track 与用于触发响应的 trigger,并返回一个一个带有 get 和 set 属性的对象
import {customRef} from 'vue';
function useDebouncedRef(value) {
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
value2=newValue
trigger()
},
}
})
}
通过customRef返回的ref对象,和正常ref对象一样,通过x.value修改或读取值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# computed
- 传入一个getter函数,返回一个默认不可手动修改的ref对象。
// 第一种写法:不可手动修改值
setup() {
let a = computed(()=>{
let total = state.supNum + state.oppNum
return ((state.supNum/total)*100).toFixed(2) + '%'
})
return{
a
}
}
//第二种写法:可以利用set修改,当a所依赖的值supNum和oppNum改变会触发get回调函数,如果修改a的值会触发set回调函数(a.vlue = a.vlue+1就会触发)
setup() {
let a = computed({
get:()=>{
let total = state.supNum + state.oppNum
return ((state.supNum/total)*100).toFixed(2) + '%'
}
set:(val)=>{
console.log(val,222)
}
})
return{
a
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# watchEffect
- 立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
setup(props){
watchEffect(()=>{
console.log(props.title)//第一次渲染的时候就会立即执行一次
})
let y = ref(0),
z = ref(0);
//如果需要监听多个值
watchEffect(()=>{
console.log(y.value,z.value)//必须写value值,因为咱们监听的是value值,不是y的ref对象
})
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# watch
- watch完全等价于2.0的watch
- watch需要侦听特定的数据源,并在回调函数中执行。
- 默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调。第一次加载页面是不执行的,和watchEffect不一样。
//侦听单个数据
setup(){
watch(state,()=>{
console.log(state.supNum)
})
//侦听state下的某个数据
watch(()=>state.supNum,()=>{
console.log('你好'+state.supNum)
})
//侦听ref创建的响应式数据,输出改变后的值和之前的值
let x = ref(0)
setup(){
watch(x,(x,prevX)=>{
console.log(x,prevX)
})
}
//侦听多个数据用数组传参,数组中任意值改变都会触发watch函数
watch([x,y],([x,y],[prevx,prevy]=>{
console.log(x,y,prevx,prevy)//prevx,prevy改变之前的值
}))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# watchEffect 和 watch 的区别
- 不需要手动传入依赖值。
- 每次初始化时会执行一次回调函数来获取依赖值。
- 无法获取原值,只能获取改变后的值。
# setup
- setup函数是一个新的组件选项,作为在组件内使用的CompositionAPI的入口点。
- 初始化props和beforeCreate之间调用
- 可以接收props和context
- this在setup中不可用,因为实在beforeCreate之前执行的,这时候还没有创建this实例。
- setup(props),props是基于Proxy代理的响应数据
setup(){
//return出去的值可以直接在html中使用,{{num}}
return{
}
}
1
2
3
4
5
6
2
3
4
5
6
# 构建响应式数据
# 方法一:ref
- 一般处理简单值的响应式,原理还是基于defineProperty监听value值
<template>
<div>
<h3>{{ title }}</h3>
<div>
<p>支持人数:{{ supNum }}</p>
<p>反对人数:{{ oppNum }}</p>
<p>支持率:</p>
<button @click="change(0)">支持</button>
<button @click="change(1)">反对</button>
</div>
</div>
</template>
// 第一种直接使用ref,比较麻烦
setup() {
let supNum = ref(0),
oppNum = ref(0);
functiom change(x){
x == 0? supNum.value++:oppNum.value++
}
return{
supNum,
oppNum,
change,
}
}
// 第二种ref,创建一个ref对象
setup(){
let state = ref({
supNum:0
oppNum:0
})
function change(x){
x == 0? state.value.supNum++ : state.value.oppNum++
}
return{
state
}
//html在使用的时候,{{state.supNum}}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 方法二:reactive
- 基于Proxy对数据进行深度的监听,以此构建响应式
- 接收一个普通对象然后返回该普通对象的响应式代理
- 响应式转换是深层的,会影对象内部所有嵌套的属性
setup() {
let state = reactive({
supNum: 0,
oppNum; 0
})
function change(x){
x == 0? state.supNum++ : state.oppNum++
// 比Object.defineProperty好用在于,对与数据或者并未初始化的对象成员,都可以随意的改变值,而且具备响应式的数据。
}
return{
...toRefs(state),
change
}
//html在使用的时候,{{supNum}}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 模板ref:获取dom元素
<template>
<div>
<div ref="root">你好</div>
</div>
</template>
export default {
setup(){
let root = ref();
onMounted(()={
console.log(root.value) //返回值是div元素
})
return{
root
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 生命周期函数
- beforeCreate=>使用setup
- create=>使用setup
- beforeMount=>onBeforeMount 第一次挂载之前
- mounted=>onMounted 第一次挂载之后
- beforeUpdate=>onBeforeUpdate 组件更新之前
- updated=>onUpdated 组件更新之后
- beforeDestroy=>onBeforeUnmount 组件销毁之前
- destoryed=>onUnmounted 组件销毁之后
- errorCaptured=>onErrorCaptured
# markRaw - 添加不可转为响应式数据的标记
- 显式标记一个对象为“永远不会转为响应式代理”,函数返回这个对象本身。
const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false
// 如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false
1
2
3
4
5
6
2
3
4
5
6
- 使用场景:markRaw 和 shallowXXX 一族的 API 允许你可选择性的覆盖 reactive readonly 默认 "深层的" 特性,或者使用无代理的普通对象。设计这种「浅层读取」有很多原因,比如:
- 一些值的实际上的用法非常简单,并没有必要转为响应式,比如某个复杂的第三方类库的实例,或者 Vue 组件对象; 当渲染一个元素数量庞大,但是数据是不可变的,跳过 Proxy 的转换可以带来性能提升。
- 仅影响在根级别:这些 API 被认为是高级的,是因为这种特性仅停留在根级别,所以如果你将一个嵌套的,没有 markRaw 的对象设置为 reactive 对象的属性,在重新访问时,你又会得到一个 Proxy 的版本,在使用中最终会导致标识混淆的严重问题:执行某个操作同时依赖于某个对象的原始版本和代理版本。
const foo = markRaw({
nested: {},
})
const bar = reactive({
// 尽管 `foo` 己经被标记为 raw 了, 但 foo.nested 并没有
nested: foo.nested,
})
console.log(foo.nested === bar.nested) // false
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 标识混淆在一般使用当中应该是非常罕见的,但是要想完全避免这样的问题,必须要对整个响应式系统的工作原理有一个相当清晰的认知。
# shallowReactive - 浅层响应式数据对象
- 只为某个对象的私有(第一层)属性创建浅层的响应式代理,不会对“属性的属性”做深层次、递归地响应式代理,而只是保留原样。
const state = shallowReactive({
foo: 1,
nested: {
bar: 2,
},
})
// 变更 state 的自有属性是响应式的
state.foo++
// ...但不会深层代理
isReactive(state.nested) // false
state.nested.bar++ // 非响应式
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# shallowReadonly - 创建浅层的只读响应式代理
- 只为某个对象的自有(第一层)属性创建浅层的只读响应式代理,同样也不会做深层次、递归地代理,深层次的属性并不是只读的。
const state = shallowReadonly({
foo: 1,
nested: {
bar: 2,
},
})
// 变更 state 的自有属性会失败
state.foo++
// ...但是嵌套的对象是可以变更的
isReadonly(state.nested) // false
state.nested.bar++ // 嵌套属性依然可修改
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# shallowRef - 创建浅层的 ref
- 创建一个 ref ,将会追踪它的 .value 更改操作,但是并不会对变更后的 .value 做响应式代理转换(即变更不会调用 reactive)
const foo = shallowRef({})
isReactive(foo.value) // false
// 更改对操作会触发响应
foo.value = {}
// 但上面新赋的这个对象并不会变为响应式对象
isReactive(foo.value) // false
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# toRaw - 响应式对象转普通对象
- 返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发更改。不建议一直持有原始对象的引用。请谨慎使用。
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
1
2
3
4
2
3
4
最近更新时间: 2021/06/17 10:13:36
- 01
- 2023/07/03 00:00:00
- 02
- 2023/04/22 00:00:00
- 03
- 2023/02/16 00:00:00