Vue3组合式API实战:构建更可维护的组件
大家好,我是蔓蔓。Vue3的组合式API是一个非常强大的特性,它让我们能够更好地组织和复用代码。今天我来和大家分享Vue3组合式API的实战技巧。
什么是组合式API
Options API vs Composition API
javascript
// Options API - 按选项组织代码
export default {
data() {
return {
count: 0,
name: '蔓蔓'
};
},
methods: {
increment() {
this.count++;
}
},
computed: {
doubleCount() {
return this.count * 2;
}
}
};
// Composition API - 按功能组织代码
import { ref, computed } from 'vue';
export default {
setup() {
const count = ref(0);
const name = ref('蔓蔓');
const increment = () => {
count.value++;
};
const doubleCount = computed(() => count.value * 2);
return { count, name, increment, doubleCount };
}
};
响应式基础
javascript
import { ref, reactive, computed, watch, watchEffect } from 'vue';
// ref - 用于基本类型
const count = ref(0);
count.value++; // 需要.value访问
// reactive - 用于对象
const state = reactive({
name: '蔓蔓',
age: 25
});
state.name = '新名字'; // 直接访问
// computed - 计算属性
const doubleCount = computed(() => count.value * 2);
// watch - 监听变化
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`);
});
// watchEffect - 自动追踪依赖
watchEffect(() => {
console.log(`count is: ${count.value}`);
});
实战案例
组合式函数
javascript
// useCounter.js - 可复用的组合式函数
import { ref, computed } from 'vue';
export function useCounter(initialValue = 0) {
const count = ref(initialValue);
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
const reset = () => {
count.value = initialValue;
};
const doubleCount = computed(() => count.value * 2);
return {
count,
increment,
decrement,
reset,
doubleCount
};
}
// 使用
import { useCounter } from './useCounter';
export default {
setup() {
const { count, increment, doubleCount } = useCounter(10);
return { count, increment, doubleCount };
}
};
异步数据获取
javascript
// useFetch.js - 数据获取组合式函数
import { ref, onMounted, onUnmounted } from 'vue';
export function useFetch(url, options = {}) {
const data = ref(null);
const error = ref(null);
const loading = ref(true);
let controller = null;
const fetchData = async () => {
loading.value = true;
error.value = null;
controller = new AbortController();
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
data.value = await response.json();
} catch (err) {
if (err.name !== 'AbortError') {
error.value = err.message;
}
} finally {
loading.value = false;
}
};
onMounted(fetchData);
onUnmounted(() => {
if (controller) {
controller.abort();
}
});
return {
data,
error,
loading,
refetch: fetchData
};
}
// 使用
import { useFetch } from './useFetch';
export default {
setup() {
const { data: user, loading, error } = useFetch('/api/user');
return { user, loading, error };
}
};
表单处理
javascript
// useForm.js - 表单处理组合式函数
import { reactive, computed } from 'vue';
export function useForm(initialValues) {
const form = reactive({ ...initialValues });
const errors = reactive({});
const validateField = (fieldName, rules) => {
errors[fieldName] = null;
for (const rule of rules) {
if (rule.required && !form[fieldName]) {
errors[fieldName] = rule.message || '此字段必填';
return false;
}
if (rule.pattern && !rule.pattern.test(form[fieldName])) {
errors[fieldName] = rule.message || '格式不正确';
return false;
}
}
return true;
};
const validate = (fieldRules) => {
let isValid = true;
for (const [fieldName, rules] of Object.entries(fieldRules)) {
if (!validateField(fieldName, rules)) {
isValid = false;
}
}
return isValid;
};
const reset = () => {
Object.assign(form, initialValues);
Object.keys(errors).forEach(key => delete errors[key]);
};
const isValid = computed(() => {
return Object.keys(errors).length === 0;
});
return {
form,
errors,
validateField,
validate,
reset,
isValid
};
}
// 使用
import { useForm } from './useForm';
export default {
setup() {
const { form, errors, validate } = useForm({
email: '',
password: ''
});
const rules = {
email: [
{ required: true, message: '邮箱必填' },
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: '请输入有效邮箱' }
],
password: [
{ required: true, message: '密码必填' },
{ pattern: /.{6,}/, message: '密码至少6位' }
]
};
const handleSubmit = () => {
if (validate(rules)) {
console.log('Form submitted:', form);
}
};
return { form, errors, handleSubmit };
}
};
高级技巧
依赖注入
javascript
// 父组件提供数据
import { provide } from 'vue';
export default {
setup() {
const theme = ref('dark');
provide('theme', theme);
provide('toggleTheme', () => {
theme.value = theme.value === 'dark' ? 'light' : 'dark';
});
}
};
// 子组件注入数据
import { inject } from 'vue';
export default {
setup() {
const theme = inject('theme');
const toggleTheme = inject('toggleTheme');
return { theme, toggleTheme };
}
};
生命周期钩子
javascript
import { onMounted, onUnmounted, onUpdated, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from 'vue';
export default {
setup() {
onBeforeMount(() => {
console.log('组件挂载前');
});
onMounted(() => {
console.log('组件挂载完成');
});
onBeforeUpdate(() => {
console.log('组件更新前');
});
onUpdated(() => {
console.log('组件更新完成');
});
onBeforeUnmount(() => {
console.log('组件卸载前');
});
onUnmounted(() => {
console.log('组件卸载完成');
});
}
};
总结
Vue3的组合式API带来了很多好处:
- 更好的代码组织 - 按功能组织代码
- 更好的代码复用 - 通过组合式函数复用逻辑
- 更好的类型推断 - 更好的TypeScript支持
- 更小的打包体积 - 按需导入
技术应当有温度,好的代码结构能提升开发体验。