1. 创建类型化的 Context Hook
首先创建 useContext.ts 文件:
import { provide, inject, reactive, readonly, InjectionKey } from 'vue';
/**
* 创建类型化的 Vue Context
* @param contextName 上下文名称(用于调试)
* @param defaultValue 默认值
* @returns [Provider组件, useContext Hook]
*/
```import { provide, inject, reactive, readonly, type VNode } from 'vue';
import type { InjectionKey, DefineComponent, PropType } from 'vue'
export function createContext<T extends object>(contextName: string, defaultValue: T) {
const ContextSymbol = Symbol(contextName) as InjectionKey<T>;
const Provider: DefineComponent<{
value: T;
}> = {
name: `${contextName}Provider`,
props: {
value: {
type: Object as PropType<T>,
required: true,
},
},
setup(props: { value: T }, { slots }: { slots: { default?: () => VNode[] | VNode } }) {
// 添加泛型约束,确保 T 是对象类型
const state = reactive<T>(props.value);
provide(ContextSymbol, readonly(state));
return () => slots.default?.();
},
};
const useContext = (): T => {
const context = inject(ContextSymbol);
if (!context) {
console.warn(`Context ${contextName} not found`);
return defaultValue;
}
return context;
};
return [Provider, useContext] as const;
}
## 2. 创建具体的类型化 Context
创建 `useCountContext.ts` 定义具体的 Context 类型:
```// src/hooks/useCountContext.ts
import { createContext } from './useContext';
// 定义 Context 类型
interface CountContextType {
count: number;
increment: () => void;
decrement: () => void;
reset?: (value: number) => void;
}
// 默认值
const defaultValue: CountContextType = {
count: 0,
increment: () => {},
decrement: () => {},
reset: (value: number) => {},
};
// 创建类型化的 CountContext
const [CountProvider, useCount] = createContext<CountContextType>('CountContext', defaultValue);
export { CountProvider, useCount };
3. 在组件中使用 (Provider)
<script setup lang="ts">
import { ref } from 'vue';
import { CountProvider } from '@/hooks/useCountContext';
import ChildComponent from './ChildComponent.vue';
const count = ref(0);
// 确保符合 CountContextType 类型
const contextValue = {
count,
increment: () => count.value++,
decrement: () => count.value--,
reset: (value: number) => {
count.value = value;
},
};
</script>
<template>
<div class="parent">
<h2>父组件 (Provider)</h2>
<p>当前计数: {{ count }}</p>
<CountProvider :value="contextValue">
<ChildComponent />
</CountProvider>
</div>
</template>
4. 在组件中使用 (Consumer)
<script setup lang="ts">
import { useCount } from '@/hooks/useCountContext';
// 自动推断出 CountContextType 类型
const { count, increment, decrement, reset } = useCount();
const handleReset = () => {
reset?.(0); // 使用可选链,因为 reset 在默认值中是可选的
};
</script>
<template>
<div class="child">
<h3>子组件 (Consumer)</h3>
<p>从 Context 获取的计数: {{ count }}</p>
<button @click="increment">增加</button>
<button @click="decrement">减少</button>
<button @click="handleReset">重置</button>
</div>
</template>
5. 嵌套 Context 示例
<script setup lang="ts">
import { ref } from 'vue';
import { CountProvider } from '@/hooks/useCountContext';
import ChildComponent from './ChildComponent.vue';
// 父级 Context
const parentCount = ref(0);
const parentContext = {
count: parentCount,
increment: () => parentCount.value++,
decrement: () => parentCount.value--,
reset: (value: number) => {
parentCount.value = value;
},
};
// 子级 Context (覆盖部分方法)
const childCount = ref(100);
const childContext = {
count: childCount,
increment: () => (childCount.value += 5), // 不同的实现
decrement: () => (childCount.value -= 5),
// 不提供 reset 方法
};
</script>
<template>
<div class="nested-example">
<h2>嵌套 Context 示例</h2>
<CountProvider :value="parentContext">
<ChildComponent />
<CountProvider :value="childContext">
<ChildComponent />
</CountProvider>
</CountProvider>
</div>
</template>
6. 高级用法 - 带参数的 Context
import { createContext } from './useContext';
interface User {
id: string;
name: string;
email: string;
}
interface AuthContextType {
user: User | null;
isAuthenticated: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const defaultValue: AuthContextType = {
user: null,
isAuthenticated: false,
login: async () => {},
logout: () => {},
};
const [AuthProvider, useAuth] = createContext<AuthContextType>('AuthContext', defaultValue);
export { AuthProvider, useAuth };
进阶技巧(Auth Context 的典型实现示例):
const useAuthProvider = () => {
const user = ref<User | null>(null);
const isAuthenticated = computed(() => !!user.value);
const login = async (email: string, password: string) => {
const res = await api.login(email, password); // 实际API调用
user.value = res.data.user;
};
const logout = () => {
await api.logout();
user.value = null;
};
return {
user: readonly(user), // 防止直接修改
isAuthenticated,
login,
logout
};
};
// 使用时
const auth = useAuthProvider();
<AuthProvider :value="auth">