闽公网安备 35020302035485号
设计模式,说白了就是写代码的“套路”。用好了,你的代码就能更健壮,更易于维护。几乎所有应用都会遇到一些共性问题,设计模式就像一个宝库,提供了很多经过验证的解决方案。今天,我就来给大家分享 12 种 Vue 设计模式,每个模式都附带一个简单的例子,让你快速上手!这只是个开胃菜,想要深入学习,还需要花更多时间研究!
<script setup lang="ts">
import { reactive, toRefs, readonly } from 'vue';
import { themes } from './utils';
// 堆代码 duidaima.com
// 1. 在模块作用域中创建全局状态,在每次使用此可组合函数时共享
const state = reactive({
darkMode: false,
sidebarCollapsed: false,
// 2. 此主题值对该可组合函数保持私有
theme: 'nord',
});
export default () => {
// 2. 仅暴露部分状态
// 使用 toRefs 允许我们共享单个值
const { darkMode, sidebarCollapsed } = toRefs(state);
// 3. 修改我们的基础状态
const changeTheme = (newTheme) => {
if (themes.includes(newTheme)) {
// 仅在它是一个有效主题时更新
state.theme = newTheme;
}
};
return {
// 2. 只返回部分状态
darkMode,
sidebarCollapsed,
// 2. 仅暴露状态的只读版本
theme: readonly(state.theme),
// 3. 我们返回一个修改基础状态的方法
changeTheme,
};
};
</script>
二. 轻量级可组合函数<script setup lang="ts">
import { ref, watch } from 'vue';
import { convertToFahrenheit } from './temperatureConversion';
export function useTemperatureConverter(celsiusRef: Ref<number>) {
const fahrenheit = ref(0);
watch(celsiusRef, (newCelsius) => {
// 实际逻辑包含在一个纯函数中
fahrenheit.value = convertToFahrenheit(newCelsius);
});
return { fahrenheit };
}
</script>
三. 谦逊组件模式<template>
<div class="max-w-sm rounded overflow-hidden shadow-lg">
<img class="w-full" :src="userData.image" alt="User Image" />
<div class="px-6 py-4">
<div class="font-bold text-xl mb-2">
{{ userData.name }}
</div>
<p class="text-gray-700 text-base">
{{ userData.bio }}
</p>
</div>
<div class="px-6 pt-4 pb-2">
<button
@click="emitEditProfile"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Edit Profile
</button>
</div>
</div>
</template>
<script setup>
defineProps({
userData: Object,
});
const emitEditProfile = () => {
emit('edit-profile');
};
</script>
四. 提取条件逻辑<!-- 之前 -->
<template>
<div v-if="condition">
<!-- 真实条件下的大量代码 -->
</div>
<div v-else>
<!-- 假设条件下的大量代码 -->
</div>
</template>
<!-- 之后 -->
<template>
<TrueConditionComponent v-if="condition" />
<FalseConditionComponent v-else />
</template>
五. 提取可组合函数<script setup lang="ts">
import { ref, watch } from 'vue';
export function useExampleLogic(initialValue: number) {
const count = ref(initialValue);
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
});
return { count, increment, decrement };
}
</script>
<template>
<div class="flex flex-col items-center justify-center">
<button
@click="decrement"
class="bg-blue-500 text-white p-2 rounded"
>
Decrement
</button>
<p class="text-lg my-4">Count: {{ count }}</p>
<button
@click="increment"
class="bg-green-500 text-white p-2 rounded"
>
Increment
</button>
</div>
</template>
<script setup lang="ts">
import { useExampleLogic } from './useExampleLogic';
const { count, increment, decrement } = useExampleLogic(0);
</script>
六. 列表组件模式<!-- 之前:在父组件中直接使用 v-for -->
<template>
<div v-for="item in list" :key="item.id">
<!-- 每个项目的代码 -->
</div>
</template>
<!-- 之后:将 v-for 抽象到子组件中 -->
<template>
<NewComponentList :list="list" />
</template>
七. 保留对象模式<!-- 使用整个对象 -->
<template>
<CustomerDisplay :customer="activeCustomer" />
</template>
<!-- CustomerDisplay.vue -->
<template>
<div>
<p>Name: {{ customer.name }}</p>
<p>Age: {{ customer.age }}</p>
<p>Address: {{ customer.address }}</p>
</div>
</template>
八. 控制器组件<!-- TaskController.vue -->
<script setup>
import useTasks from './composables/useTasks';
// 可组合函数包含业务逻辑
const { tasks, addTask, removeTask } = useTasks();
</script>
<template>
<!-- 谦逊组件提供 UI -->
<TaskInput @add-task="addTask" />
<TaskList :tasks="tasks" @remove-task="removeTask" />
</template>
九. 策略模式<template>
<component :is="currentComponent" />
</template>
<script setup>
import { computed } from 'vue';
import ComponentOne from './ComponentOne.vue';
import ComponentTwo from './ComponentTwo.vue';
import ComponentThree from './ComponentThree.vue';
const props = defineProps({
conditionType: String,
});
const currentComponent = computed(() => {
switch (props.conditionType) {
case 'one':
return ComponentOne;
case 'two':
return ComponentTwo;
case 'three':
return ComponentThree;
default:
return DefaultComponent;
}
});
</script>
十. 隐藏组件模式<!-- 重构之前 -->
<template>
<!-- 实际上是一个“图表”组件 -->
<DataDisplay
:chart-data="data"
:chart-options="chartOptions"
/>
<!-- 实际上是一个“表格”组件 -->
<DataDisplay
:table-data="data"
:table-settings="tableSettings"
/>
</template>
<!-- 重构之后 -->
<template>
<Chart :data="data" :options="chartOptions" />
<table :data="data" :settings="tableSettings" />
</template>
十一. 内部交易模式<!-- ParentComponent.vue -->
<template>
<div>
<!-- 这个组件使用来自父组件的所有内容。
它起什么作用呢? -->
<ChildComponent
:user-name="userName"
:email-address="emailAddress"
:phone-number="phoneNumber"
@user-update="(val) => $emit('user-update', val)"
@email-update="(val) => $emit('email-update', val)"
@phone-update="(val) => $emit('phone-update', val)"
/>
</div>
</template>
<script setup>
defineProps({
userName: String,
emailAddress: String,
phoneNumber: String,
});
defineEmits(['user-update', 'email-update', 'phone-update']);
</script>
十二. 长组件模式<!-- 之前:一个冗长且复杂的组件 -->
<template>
<div>
<!-- 大量 HTML 和逻辑 -->
</div>
</template>
<!-- 之后:分解成更小的组件,
名称告诉你代码的作用。 -->
<template>
<ComponentPartOne />
<ComponentPartTwo />
</template>
总结