Vue.js --- Vue3中其他组合式API
1. 前言
组合式API可以让我们更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。组合式 API 提供了更灵活的方式来组织和复用代码。在选项式 API 中,组件的逻辑是按照生命周期钩子、计算属性、方法等进行组织的,这在某些情况下可能会导致功能之间的耦合。而组合式 API 允许将相关的功能组织到一个或多个“组合函数”(composables)中,并在不同组件中复用。
2. 浅层次响应
2.1 shallowReactive
与reactive不同的是,shallowReactive只会对对象的第一层属性进行响应式处理,而不会递归地处理深层的嵌套属性。也就是说,只有顶层属性会被代理,深层次的对象则不会被代理
import { shallowReactive } from 'vue';
const state = shallowReactive({
a: 1,
b: { x: 10, y: 20 }
});
state.a = 2; // 响应式,修改会触发视图更新
state.b.x = 30; // 非响应式,修改不会触发视图更新
2.2 readonly
用于创建一个只读的响应式对象,通过 readonly
创建的对象,所有属性都变成了只读的,即不能对该对象进行修改。如果尝试修改它的属性,Vue 会在开发环境中发出警告,但不会抛出错误。
import { readonly } from 'vue';
const state = readonly({
a: 1,
b: { x: 10, y: 20 }
});
state.a = 2; // 会发出警告,不能修改属性
state.b.x = 30; // 会发出警告,不能修改嵌套属性
2.3 shallowReadonly
与readonly类似,但可以看出多了一个shallow,也只会局限于第一层属性,而不会递归处理、
更深层属性仍可以被修改,只有顶层属性是只读的
import { shallowReadonly } from 'vue';
const state = shallowReadonly({
a: 1,
b: { x: 10, y: 20 }
});
state.a = 2; // 会发出警告,不能修改顶层属性
state.b.x = 30; // 不会发出警告,可以修改嵌套属性
3. 数据代理
3.1 toRaw
用来获取响应式对象的原始非响应式版本,当你在 Vue 中创建一个响应式对象时,Vue 会为该对象添加代理,进行数据的响应式追踪。而有时你可能需要访问该对象的原始数据,toRaw
就是用来实现这个功能的。
简单来讲就是储存原始的响应式对象的数据,随便我怎么改变响应式数据,toRaw的对象都是返回原始数据
import { reactive, toRaw } from 'vue';
const state = reactive({
a: 1,
b: { x: 10, y: 20 }
});
const rawState = toRaw(state);
state.a = 2; // 修改响应式对象
console.log(rawState.a); // 输出 1,原始数据不受影响
3.2 markRaw
用来标记某个对象不需要被 Vue 代理。当你想让某个对象避免成为响应式对象时,可以使用 markRaw
。该对象会被 Vue 视为普通的非响应式对象,Vue 不会为其创建代理,也不会进行响应式追踪。
import { markRaw } from 'vue';
const rawObject = markRaw({
a: 1,
b: { x: 10, y: 20 }
});
console.log(rawObject); // 不会被 Vue 代理,保持原始状态
4. 自定义响应 customRef
4.1 声明
customRef是一个允许开发者自定义响应式引用的 API。它使你能够控制响应式对象的 getter 和 setter 行为,提供了更灵活的方式来处理响应式数据。与 Vue 内置的 ref
不同,customRef
允许你定义如何在响应式更新过程中处理数据。
通过设置getter去获取数据,setter去设置数据
声明
import { customRef } from 'vue';
export default {
setup() {
const value = customRef((track, trigger) => {
let internalValue = 0;
return {
get() {
track(); // 触发依赖追踪
return internalValue;
},
set(newValue) {
if (Math.abs(newValue - internalValue) > 5) { // 设置条件
internalValue = newValue;
trigger(); // 触发视图更新
}
}
};
});
return {
value
};
}
};
track与trigger是由Vue传入的内部函数,该函数返回一个包含get,set的对象。
track用于追踪依赖,可以理解为找到目标数据
trigger用于在数据更新时触发视图更新
4.2 调用
想要调用或者修改customRef对象中的数值,需要使用后缀.value的方式
value.value= 10;
例子:
<template>
<h1>{{value}}</h1>
<button @click="changeValue">点我改变数值</button>
</template>
<style>
</style>
<script>
import {customRef} from 'vue'
export default {
setup() {
let sum = 0;
const value = customRef((track,trigger) => {
let interValue = 0;
return {
get() {
track();
return interValue;
},
set(newValue) {
if (newValue > interValue) {
interValue = newValue;
trigger();
}
}
}
})
function changeValue() {
value.value = 50;
}
return {sum,value,changeValue}
}
}
</script>
5. 数据传递 provide与inject
provide:在父组件中使用,目的是将数据提供给后代组件。它将某些数据提供到组件树中,所有嵌套的子组件(不论是直接子组件还是间接子组件)都可以通过 inject
来访问这些数据。
inject:在子组件中使用,用来接收父组件通过 provide
提供的数据。inject
使得子组件可以访问到父组件提供的特定数据,而不需要通过逐层的 props 传递。
相比以往的props传参,vuex定义全局参数,这样更加的方便快捷
父组件提供数据
import {provide} from 'vue'
export default{
setup(){
provide('myValue',''HelloWorld);
}
}
结构体为(参数名,参数内容)
子组件接受数据
inject后直接接受参数名即可
import {inject} from 'vue'
export default()
{
setup(){
const injectedValue = inject('myValue')
return {injectedValue}
}
}
6. 响应式数据的判断
isRef() | 判断是否为响应数据 |
isReactive() | 判断是否为响应对象 |
isReadonly() | 判断是否为只读属性 |
isProxy() | 判断是否为响应式系统,包括ref,reactive |
7. 渲染DOM组件 - Teleport组件
Teleport是一个非常实用的组件,允许将子组件的内容渲染到DOM中不同的位置,而不依赖于父组件的嵌套结构。对于管理模态框,弹出层,工具提示,通知等UI元素非常有用,特别是当这些元素需要在DOM的不同位置进行渲染时,避免了许多的嵌套和复杂的样式问题
例如,消息通知栏常常需要放置在顶部,但又受到某一子组件的控制,通过Teleport组件控制标签位置到指定目标容器
格式:
<teleport to="body">
</teleport>
to指向目标容器
示例:模拟弹窗
<template>
<div>
<h1>Vue3 Teleport 示例</h1>
<button @click="closeModel">点我</button>
<teleport to="body">
<div class="model" v-if="controller">
<p>这是一个模板!</p>
<button @click="closeModel">关闭</button>
</div>
</teleport>
</div>
</template>
<style>
.model{
position:fixed;
top:50%;
left:50%;
background-color:yellow;
border-radius:8px;
}
</style>
<script>
export default {
data() {
return {
controller:false
}
},
methods: {
closeModel() {
this.controller = !this.controller
}
}
}
</script>
8. 异步组件
8.1 异步加载组件
让我们先来了解异步加载组件
它允许我们根据需要加载和渲染组件,而不是在应用加载时一次性加载所有组件。这样可以显著提高应用的性能,尤其是在大型应用中,避免一次性加载所有的 JavaScript 代码和组件。
import { defineAsyncComponent } from 'vue';
export default {
components: {
AsyncComponent: defineAsyncComponent(() =>
import('./AsyncComponent.vue') // 异步加载组件
)
}
}
在defineAsyncComponent方法中传入一个函数
suspense组件用于处理异步组件的加载和显示一个加载状态。它为开发者提供了一种优雅的方式来处理异步数据加载的状态,特别是在处理组件加载、API 请求或其他需要等待的操作时,Suspense
让应用能够在等待数据或资源时优雅地过渡。
在vue中还提供了异步组件的加载状态,加载失败和超时处理的功能
const AsyncComponent = () => ({
// 需要加载的组件 (should be a promise)
component: import('./components/AsyncComponent.vue'),
// 加载中时的组件
loading: LoadingComponent,
// 加载失败时的组件
error: ErrorComponent,
// 超过时间后显示的组件
timeout: 3000
});
8.2 suspense结构
default插槽:用于放置异步组件,vue会等待其最终的加载完成
fallback插槽:当异步组件正在加载时,会显示fallback插槽中的内容,通常是文字或者加载动画
<template>
<Suspense>
<!-- 异步组件 -->
<template #default>
<AsyncComponent />
</template>
<!-- 加载时显示的内容 -->
<template #fallback>
<p>加载中...</p>
</template>
</Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue';
export default {
components: {
AsyncComponent: defineAsyncComponent(() =>
import('./AsyncComponent.vue') // 异步加载组件
)
}
}
</script>