vue-23(创建用于逻辑提取的可重用组合组件)
创建用于逻辑提取的可重用组合组件
可重用的组合式是 Vue 组合式 API 的基石,它使你能够在多个组件中提取和重用有状态逻辑。这有助于编写更清晰的代码,减少冗余,并提高可维护性。通过将特定功能封装到组合式中,你可以轻松地共享和管理复杂的逻辑,使你的 Vue 应用更具可扩展性和组织性。本课程将深入探讨创建有效且可重用组合式的原则和技术。
理解组合式
从本质上讲,组合式是一个封装并返回可在多个组件中使用的有状态逻辑的函数。它利用组合式 API 的特性,如 ref
、reactive
和生命周期钩子来管理状态和副作用。主要目标是将从组件中提取的复杂逻辑转化为可重用的单元,从而推广 DRY(不要重复自己)原则。
可重用的原则
要创建真正可重用的组合组件,请考虑这些原则:
- 单一职责原则: 每个可组合项应专注于一个特定且明确定义的任务。避免创建处理多个不相关功能的"上帝"可组合项。
- 配置选项: 允许组件通过选项或参数来配置可组合组件的行为。这使得可组合组件更能适应不同的使用场景。
- 清晰的 API: 为可组合组件定义一个清晰简洁的 API,使其他开发者易于理解和使用。
- 可测试性: 确保该组合件能够独立于其他部分轻松进行测试。这有助于保持其质量并防止回归问题。
- 解耦: Composables 应与特定组件或应用逻辑解耦。它们应设计得尽可能通用。
基本示例:useMouse
让我们从一个简单的例子开始:一个跟踪鼠标位置的组合式组件。
// composables/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue';export function useMouse() {const x = ref(0);const y = ref(0);function update(event) {x.value = event.pageX;y.value = event.pageY;}onMounted(() => {window.addEventListener('mousemove', update);});onUnmounted(() => {window.removeEventListener('mousemove', update);});return { x, y };
}
解释:
ref
: 为 x 和 y 坐标创建响应式引用。update
: 一个根据鼠标事件更新 x 和 y 坐标的函数。onMounted
: 组件挂载时注册mousemove
事件监听器。onUnmounted
: 组件卸载时移除事件监听器以防止内存泄漏。return
: 返回一个包含响应式 x 和 y 坐标的对象,可在组件模板中使用。
在组件中使用:
<template><p>Mouse position: x={{ x }}, y={{ y }}</p>
</template><script>
import { useMouse } from '../composables/useMouse';export default {setup() {const { x, y } = useMouse();return { x, y };}
};
</script>
高级组合式:逻辑提取
现在,让我们探索更复杂的场景,其中可组合组件可以显著提高代码的组织性和可重用性。
示例:useFetch
在 Web 应用程序中,从 API 获取数据是一项常见任务。让我们创建一个 useFetch
可组合组件来处理这个任务。
// composables/useFetch.js
import { ref, reactive, toRefs } from 'vue';export function useFetch(url) {const data = ref(null);const error = ref(null);const loading = ref(false);const state = reactive({data,error,loading})const fetchData = async () => {loading.value = true;try {const response = await fetch(url);if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}data.value = await response.json();} catch (err) {error.value = err;} finally {loading.value = false;}};fetchData(); // Immediately fetch data when the composable is usedreturn {...toRefs(state),fetchData // Expose the fetchData function to allow manual re-fetching};
}
解释:
数据
,错误
,加载中
:用于存储获取的数据、发生的任何错误以及加载状态的响应式引用。fetchData
: 一个异步函数,从给定 URL 获取数据,处理错误,并更新响应式引用。toRefs
: 将响应式对象state
转换为 ref 对象,允许在组件中进行解构。- 当可组合函数被使用时,
fetchData
函数会立即被调用,启动数据获取过程。 fetchData
函数也会被返回,允许组件在需要时手动触发重新获取数据。
在组件中使用:
<template><div v-if="loading">Loading...</div><div v-else-if="error">{{ error.message }}</div><div v-else><h1>{{ data.title }}</h1><p>{{ data.body }}</p><button @click="fetchData">Refresh</button></div>
</template><script>
import { useFetch } from '../composables/useFetch';export default {setup() {const { data, error, loading, fetchData } = useFetch('https://jsonplaceholder.typicode.com/posts/1');return { data, error, loading, fetchData };}
};
</script>
示例:useLocalStorage
另一个常见需求是在本地存储中持久化数据。让我们创建一个 useLocalStorage
组合式。
// composables/useLocalStorage.js
import { ref, watch, onMounted } from 'vue';export function useLocalStorage(key, defaultValue = null) {const storedValue = ref(defaultValue);onMounted(() => {// Initialize from localStorage on mountconst item = localStorage.getItem(key);if (item) {try {storedValue.value = JSON.parse(item);} catch (error) {console.error("Error parsing stored value:", error);// If parsing fails, leave it as default or handle as needed}}});watch(storedValue,(newValue) => {localStorage.setItem(key, JSON.stringify(newValue));},{ deep: true } // Watch for changes in nested objects/arrays);return storedValue;
}
解释:
storedValue
: 一个反应式引用,用于存储本地存储中的值。onMounted
: 在组件挂载时从本地存储中获取值,并初始化storedValue
。watch
: 监听storedValue
的变化并相应地更新本地存储。deep: true
选项确保可以检测到嵌套对象或数组的更改。- 包含错误处理,以便在存储的值不是有效 JSON 的情况下优雅地处理。
在组件中使用:
<template><div><input v-model="name" placeholder="Enter your name"><p>Hello, {{ name }}!</p></div>
</template><script>
import { useLocalStorage } from '../composables/useLocalStorage';export default {setup() {const name = useLocalStorage('name', '');return { name };}
};
</script>
处理配置选项
为了让可组合项更加灵活,你可以将配置选项作为参数传递。例如,让我们修改 useFetch
可组合项,使其接受一个选项对象。
// composables/useFetch.js
import { ref, reactive, toRefs } from 'vue';export function useFetch(url, options = {}) {const data = ref(null);const error = ref(null);const loading = ref(false);const state = reactive({data,error,loading})const fetchData = async () => {loading.value = true;try {const response = await fetch(url, options); // Pass options to fetchif (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}data.value = await response.json();} catch (err) {error.value = err;} finally {loading.value = false;}};fetchData();return {...toRefs(state),fetchData};
}
在组件中使用:
<template><div v-if="loading">Loading...</div><div v-else-if="error">{{ error.message }}</div><div v-else><h1>{{ data.title }}</h1><p>{{ data.body }}</p></div>
</template><script>
import { useFetch } from '../composables/useFetch';export default {setup() {const options = {headers: {'Authorization': 'Bearer my-token'}};const { data, error, loading } = useFetch('https://jsonplaceholder.typicode.com/posts/1', options);return { data, error, loading };}
};
</script>
在这个示例中,useFetch
组合函数现在接受一个直接传递给 fetch
函数的 options
对象,允许你配置请求头、方法和其他请求参数。