文章

Vue3基础——其他API

shallowRef与shallowReactive

对于深层次的对象,shallowRef与shallowReactive只将最外层的对象转换为响应式数据,用于对大型数据结构的性能优化或是与外部的状态管理系统集成

  • shallowRef:只对.value的访问是响应式的
  • shallowReactive:内部的ref数据不会被自动解包
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
// shallowRef
const state = shallowRef({ count: 1 })

// 不会触发更改
state.value.count = 2

// 会触发更改
state.value = { count: 2 }

// shallowReactive
const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 更改状态自身的属性是响应式的
state.foo++

// ...但下层嵌套对象不会被转为响应式
isReactive(state.nested) // false

// 不是响应式的
state.nested.bar++

readonly与shallowReadonly

  • readonly:创建一个响应式数据的只读响应式副本,随原数据变化触发响应式更新
  • shallowReadonly:只将最外层对象的属性变为了只读
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
// readonly
const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
  // 用来做响应性追踪
  console.log(copy.count)
})

// 更改源属性会触发其依赖的侦听器
original.count++

// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!

// shallowReadonly
const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 更改状态自身的属性会失败
state.foo++

// ...但可以更改下层嵌套对象
isReadonly(state.nested) // false

// 这是可以通过的
state.nested.bar++

toRaw与markRaw

  • toRaw:返回响应式数据的原始数据
  • markRaw:将一个对象标记为不可被转为代理,返回该对象本身
1
2
3
4
5
6
7
8
9
10
11
12
13
// toRaw
const foo = {}
const reactiveFoo = reactive(foo)

console.log(toRaw(reactiveFoo) === foo) // true

// markRaw
const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false

// 也适用于嵌套在其他响应性对象
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false

customRef

创建一个自定义的ref,显式声明对其依赖追踪和更新触发的控制方式

函数声明

1
2
3
4
5
6
7
8
9
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>

type CustomRefFactory<T> = (
  track: () => void,
  trigger: () => void
) => {
  get: () => T
  set: (value: T) => void
}
  • customRef接收一个factory函数,返回一个ref数据
  • factory函数接收track和trigger两个函数作为参数,返回一个带有get方法和set方法的对象

防抖ref实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { customRef } from 'vue'

export function useDebouncedRef(value: string, delay: number = 200) {
  let timeout: number
  return customRef((track, trigger) => {
    return {
      // 在数据被读取时调用
      get() {
        // 标记引用
        track()
        return value
      },
      // 在数据被修改时调用
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          // 触发更新
          trigger()
        }, delay)
      }
    }
  })
}

Teleport

用于改变组件模板内容的实际DOM渲染位置,而不改变组件的逻辑关系,可用于实现模态框等效果

e.g. 实现一个模态框

一个outer块中包含MyModal组件,在逻辑上为父子关系

1
2
3
4
5
6
<div class="outer">
  <h3>Tooltips with Vue 3 Teleport</h3>
  <div>
    <MyModal />
  </div>
</div>

在编写css布局时,通常将模态框显示在整个视窗的中间,但由于父子关系,outer块的布局可能会导致MyModal的定位出现问题,MyModal的DOM位置应该脱离outer块,因此使用Teleport实现MyModal,改变实际DOM位置

1
2
3
4
5
6
7
8
9
<button @click="open = true">Open Modal</button>

<!--将模态框内容渲染到body块中-->
<Teleport to="body">
  <div v-if="open" class="modal">
    <p>Hello from the modal!</p>
    <button @click="open = false">Close</button>
  </div>
</Teleport>
本文由作者按照 CC BY 4.0 进行授权