loading

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

# toRef

  • 将参数转化为ref对象,例如
let a = 0;
console.log(toRef(a)) //ObjectRefImpl {_object: 0, _key: undefined, __v_isRef: true}
1
2

# toRefs

  • 把响应式对象转为普通对象,该普通对象每一个property都是一个ref,和响应式对象property一一对应。

# isRef

  • 判断该值是否为ref对象,如果是则返回true,否则是false
    let a = 0
    console.log(isRef(a)) // false
1
2

# isProxy

  • 检查一个对象是否由reactive或者readonly方法创建的代理
    let a = 0
    console.log(isRef(a)) // false
1
2

# isReactive

  • 检查一个对象是否由reactive创建的响应式代理。
  • 如果这个代理是由reactive创建的,但是又被reactive创建的另一个代理包裹了一层,那么同样会返回true
    let a = 0
    console.log(isRef(a)) // false
1
2

# readonly

  • 传入一个对象(响应式或者普通)或ref,返回一个原始对象的只读代理,一个只读的代理是深层的,对象内部任何嵌套的属性也只是只读的。

# isReaonly

  • 检查一个对象是否由readonly创建的只读代理。。
  • 如果这个代理是由reactive创建的,但是又被reactive创建的另一个代理包裹了一层,那么同样会返回true
    let a = 0
    console.log(isRef(a)) // false
1
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

# 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

# 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

# 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

# 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

# 构建响应式数据

# 方法一: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

# 方法二: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

# 模板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

# 生命周期函数

  • 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
  • 使用场景: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
  • 标识混淆在一般使用当中应该是非常罕见的,但是要想完全避免这样的问题,必须要对整个响应式系统的工作原理有一个相当清晰的认知。

# 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

# 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

# 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

# toRaw - 响应式对象转普通对象

  • 返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发更改。不建议一直持有原始对象的引用。请谨慎使用。
const foo = {}
const reactiveFoo = reactive(foo)

console.log(toRaw(reactiveFoo) === foo) // true
1
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