Files
vue3-tutorial/docs/chapter4/02_Composition API_其它部分.md
zxfjd3g 832b961d34 doc
2020-11-12 21:07:26 +08:00

465 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2. Composition API(其它部分)
## 1) shallowReactive 与 shallowRef
- shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)
- shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
- 什么时候用浅响应式呢?
- 一般情况下使用ref和reactive即可
- 如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
- 如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef
```vue
<template>
<h2>App</h2>
<h3>m1: {{m1}}</h3>
<h3>m2: {{m2}}</h3>
<h3>m3: {{m3}}</h3>
<h3>m4: {{m4}}</h3>
<button @click="update">更新</button>
</template>
<script lang="ts">
import { reactive, ref, shallowReactive, shallowRef } from 'vue'
/*
shallowReactive与shallowRef
shallowReactive: 只处理了对象内最外层属性的响应式(也就是浅响应式)
shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
总结:
reactive与ref实现的是深度响应式, 而shallowReactive与shallowRef是浅响应式
什么时候用浅响应式呢?
一般情况下使用ref和reactive即可,
如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef
*/
export default {
setup () {
const m1 = reactive({a: 1, b: {c: 2}})
const m2 = shallowReactive({a: 1, b: {c: 2}})
const m3 = ref({a: 1, b: {c: 2}})
const m4 = shallowRef({a: 1, b: {c: 2}})
const update = () => {
// m1.b.c += 1
// m2.b.c += 1
// m3.value.a += 1
m4.value.a += 1
}
return {
m1,
m2,
m3,
m4,
update,
}
}
}
</script>
```
## 2) readonly 与 shallowReadonly
- readonly:
- 深度只读数据
- 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。
- 只读代理是深层的:访问的任何嵌套 property 也是只读的。
- shallowReadonly
- 浅只读数据
- 创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
- 应用场景:
- 在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除
```vue
<template>
<h2>App</h2>
<h3>{{state}}</h3>
<button @click="update">更新</button>
</template>
<script lang="ts">
import { reactive, readonly, shallowReadonly } from 'vue'
/*
readonly: 深度只读数据
获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。
只读代理是深层的:访问的任何嵌套 property 也是只读的。
shallowReadonly: 浅只读数据
创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
应用场景:
在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除
*/
export default {
setup () {
const state = reactive({
a: 1,
b: {
c: 2
}
})
// const rState1 = readonly(state)
const rState2 = shallowReadonly(state)
const update = () => {
// rState1.a++ // error
// rState1.b.c++ // error
// rState2.a++ // error
rState2.b.c++
}
return {
state,
update
}
}
}
</script>
```
## 3) toRaw 与 markRaw
- toRaw
- 返回由 `reactive``readonly` 方法转换成响应式代理的普通对象。
- 这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发界面更新。
- markRaw
- 标记一个对象,使其永远不会转换为代理。返回对象本身
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类实例或 Vue 组件对象。
- 当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。
```vue
<template>
<h2>{{state}}</h2>
<button @click="testToRaw">测试toRaw</button>
<button @click="testMarkRaw">测试markRaw</button>
</template>
<script lang="ts">
/*
toRaw: 得到reactive代理对象的目标数据对象
*/
import {
markRaw,
reactive, toRaw,
} from 'vue'
export default {
setup () {
const state = reactive<any>({
name: 'tom',
age: 25,
})
const testToRaw = () => {
const user = toRaw(state)
user.age++ // 界面不会更新
}
const testMarkRaw = () => {
const likes = ['a', 'b']
// state.likes = likes
state.likes = markRaw(likes) // likes数组就不再是响应式的了
setTimeout(() => {
state.likes[0] += '--'
}, 1000)
}
return {
state,
testToRaw,
testMarkRaw,
}
}
}
</script>
```
## 4) toRef
- 为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
- 区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
- 应用: 当要将 某个prop 的 ref 传递给复合函数时toRef 很有用
```vue
<template>
<h2>App</h2>
<p>{{state}}</p>
<p>{{foo}}</p>
<p>{{foo2}}</p>
<button @click="update">更新</button>
<Child :foo="foo"/>
</template>
<script lang="ts">
/*
toRef:
为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
应用: 当要将某个 prop 的 ref 传递给复合函数时toRef 很有用
*/
import {
reactive,
toRef,
ref,
} from 'vue'
import Child from './Child.vue'
export default {
setup () {
const state = reactive({
foo: 1,
bar: 2
})
const foo = toRef(state, 'foo')
const foo2 = ref(state.foo)
const update = () => {
state.foo++
// foo.value++
// foo2.value++ // foo和state中的数据不会更新
}
return {
state,
foo,
foo2,
update,
}
},
components: {
Child
}
}
</script>
```
```vue
<template>
<h2>Child</h2>
<h3>{{foo}}</h3>
<h3>{{length}}</h3>
</template>
<script lang="ts">
import { computed, defineComponent, Ref, toRef } from 'vue'
const component = defineComponent({
props: {
foo: {
type: Number,
require: true
}
},
setup (props, context) {
const length = useFeatureX(toRef(props, 'foo'))
return {
length
}
}
})
function useFeatureX(foo: Ref) {
const lenth = computed(() => foo.value.length)
return lenth
}
export default component
</script>
```
## 5) customRef
- 创建一个自定义的 ref并对其依赖项跟踪和更新触发进行显式控制
- 需求: 使用 customRef 实现 debounce 的示例
```vue
<template>
<h2>App</h2>
<input v-model="keyword" placeholder="搜索关键字"/>
<p>{{keyword}}</p>
</template>
<script lang="ts">
/*
customRef:
创建一个自定义的 ref并对其依赖项跟踪和更新触发进行显式控制
需求:
使用 customRef 实现 debounce 的示例
*/
import {
ref,
customRef
} from 'vue'
export default {
setup () {
const keyword = useDebouncedRef('', 500)
console.log(keyword)
return {
keyword
}
},
}
/*
实现函数防抖的自定义ref
*/
function useDebouncedRef<T>(value: T, delay = 200) {
let timeout: number
return customRef((track, trigger) => {
return {
get() {
// 告诉Vue追踪数据
track()
return value
},
set(newValue: T) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
// 告诉Vue去触发界面更新
trigger()
}, delay)
}
}
})
}
</script>
```
## 6) provide 与 inject
- provide` 和 `inject` 提供依赖注入,功能类似 2.x 的 `provide/inject
- 实现跨层级组件(祖孙)间通信
```vue
<template>
<h1>父组件</h1>
<p>当前颜色: {{color}}</p>
<button @click="color='red'"></button>
<button @click="color='yellow'"></button>
<button @click="color='blue'"></button>
<hr>
<Son />
</template>
<script lang="ts">
import { provide, ref } from 'vue'
/*
- provide` 和 `inject` 提供依赖注入,功能类似 2.x 的 `provide/inject
- 实现跨层级组件(祖孙)间通信
*/
import Son from './Son.vue'
export default {
name: 'ProvideInject',
components: {
Son
},
setup() {
const color = ref('red')
provide('color', color)
return {
color
}
}
}
</script>
```
```vue
<template>
<div>
<h2>子组件</h2>
<hr>
<GrandSon />
</div>
</template>
<script lang="ts">
import GrandSon from './GrandSon.vue'
export default {
components: {
GrandSon
},
}
</script>
```
```vue
<template>
<h3 :style="{color}">孙子组件: {{color}}</h3>
</template>
<script lang="ts">
import { inject } from 'vue'
export default {
setup() {
const color = inject('color')
return {
color
}
}
}
</script>
```
## 7) 响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由 `reactive` 创建的响应式代理
- isReadonly: 检查一个对象是否是由 `readonly` 创建的只读代理
- isProxy: 检查一个对象是否是由 `reactive` 或者 `readonly` 方法创建的代理