465 lines
9.3 KiB
Markdown
465 lines
9.3 KiB
Markdown
# 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` 方法创建的代理
|
||
|