Composition API(其它部分)

shallowReactive 与 shallowRef
shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)

shallowRef: 只处理了value的响应式, 不进行对象的reactive处理

什么时候用浅响应式呢?

一般情况下使用ref和reactive即可
如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef
<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 为只读,但不执行嵌套对象的深度只读转换
应用场景:
在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除
<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 组件对象。
当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。
<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 很有用
<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>

<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 的示例
<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

实现跨层级组件(祖孙)间通信

<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>
<template>
 <div>
   <h2>子组件</h2>
   <hr>
   <GrandSon />
 </div>
</template>

<script lang="ts">
import GrandSon from './GrandSon.vue'
export default {
 components: {
   GrandSon
 },
}
</script>
<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 方法创建的代理

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):