Vue3 + TypeScript provide/inject 小白学习笔记

一、这是什么?为什么需要?

1.1 简单理解

  • provide(提供):爷爷组件说:"我这里有数据,子孙们随便用!"

  • inject(注入):孙子组件说:"我要用爷爷提供的数据!"

1.2 解决的问题

没有 provide/inject 时

text

复制代码
爷爷组件 → 爸爸组件 → 儿子组件 → 孙子组件
    ↓         ↓         ↓         ↓
  数据      props     props     props (需要层层传递,好麻烦!)

使用 provide/inject 后

text

复制代码
爷爷组件(provide数据)
    ↓
孙子组件(inject数据)直接跳过中间商!

二、基础使用方法

2.1 提供数据(爷爷组件)

vue

复制代码
<template>
  <div>
    <ParentComponent />
  </div>
</template>

<script setup lang="ts">
import { ref, provide } from 'vue'
import ParentComponent from './ParentComponent.vue'

// 1. 准备要提供的数据
const userInfo = ref({
  name: '张三',
  age: 25
})

const theme = ref('dark')

// 2. 提供数据给子孙组件
provide('userInfo', userInfo)
provide('theme', theme)
</script>

2.2 注入数据(孙子组件)

vue

复制代码
<template>
  <div>
    <h1>用户信息:{{ userInfo.name }}</h1>
    <p>主题:{{ theme }}</p>
  </div>
</template>

<script setup lang="ts">
import { inject } from 'vue'

// 注入爷爷提供的数据
const userInfo = inject('userInfo')
const theme = inject('theme')
</script>

三、TypeScript 类型安全写法

3.1 基础类型定义

vue

复制代码
<script setup lang="ts">
import { ref, provide, inject } from 'vue'

// 定义接口
interface User {
  name: string
  age: number
  email?: string
}

// 提供数据时指定类型
const userInfo = ref<User>({
  name: '李四',
  age: 30
})

provide('userInfo', userInfo)

// 注入时指定类型
const userInfo = inject<User>('userInfo')
</script>

3.2 更安全的写法(推荐)

vue

复制代码
<script setup lang="ts">
import { inject, ref, provide } from 'vue'

interface Theme {
  mode: 'light' | 'dark'
  primaryColor: string
}

// 提供默认值,避免 undefined
const defaultTheme: Theme = {
  mode: 'light',
  primaryColor: '#1890ff'
}

const theme = ref<Theme>(defaultTheme)
provide('theme', theme)

// 注入时提供默认值
const theme = inject<Theme>('theme', defaultTheme)

3.3 使用 Symbol 避免命名冲突(高级用法)

ts

复制代码
// types.ts 或 constants.ts
export const THEME_KEY = Symbol('theme') as InjectionKey<Theme>
export const USER_KEY = Symbol('user') as InjectionKey<User>

// 爷爷组件
import { THEME_KEY, USER_KEY } from './types'
provide(THEME_KEY, theme)
provide(USER_KEY, userInfo)

// 孙子组件
const theme = inject(THEME_KEY)
const userInfo = inject(USER_KEY)

四、实际项目完整示例

4.1 用户信息共享

vue

复制代码
<!-- App.vue (根组件) -->
<template>
  <div :class="`app ${theme}`">
    <Header />
    <Content />
  </div>
</template>

<script setup lang="ts">
import { ref, provide } from 'vue'
import Header from './Header.vue'
import Content from './Content.vue'

interface AppState {
  user: {
    name: string
    role: string
  }
  theme: 'light' | 'dark'
  isLogin: boolean
}

const appState = ref<AppState>({
  user: {
    name: '王五',
    role: 'admin'
  },
  theme: 'light',
  isLogin: true
})

// 提供整个应用状态
provide('appState', appState)

// 提供修改主题的方法
const toggleTheme = () => {
  appState.value.theme = appState.value.theme === 'light' ? 'dark' : 'light'
}
provide('toggleTheme', toggleTheme)
</script>

<style>
.light { background: white; color: black; }
.dark { background: #333; color: white; }
</style>

vue

复制代码
<!-- Header.vue (头部组件) -->
<template>
  <header>
    <h1>欢迎,{{ appState.user.name }}</h1>
    <button @click="toggleTheme">
      切换主题:{{ appState.theme }}
    </button>
  </header>
</template>

<script setup lang="ts">
import { inject } from 'vue'

interface AppState {
  user: {
    name: string
    role: string
  }
  theme: 'light' | 'dark'
  isLogin: boolean
}

// 注入数据和方法
const appState = inject<AppState>('appState')!
const toggleTheme = inject<() => void>('toggleTheme')!
</script>

五、响应式数据更新

5.1 响应式原理

vue

复制代码
<script setup lang="ts">
import { ref, provide } from 'vue'

// 使用 ref 创建响应式数据
const count = ref(0)

// 提供响应式数据
provide('count', count)

// 在任意组件中修改,所有注入的地方都会更新
const increase = () => {
  count.value++  // 修改这里,所有注入 count 的组件都会更新
}
provide('increase', increase)
</script>

5.2 在子组件中修改

vue

复制代码
<template>
  <div>
    <p>计数:{{ count }}</p>
    <button @click="increase">+1</button>
  </div>
</template>

<script setup lang="ts">
import { inject } from 'vue'

const count = inject<number>('count')!
const increase = inject<() => void>('increase')!
</script>

六、常见问题及解决方法

6.1 问题1:inject 返回 undefined

错误

ts

复制代码
const data = inject('someKey') // 可能是 undefined
console.log(data.value) // 报错!

解决

ts

复制代码
// 方法1:提供默认值
const data = inject('someKey', defaultValue)

// 方法2:类型断言
const data = inject('someKey')!

// 方法3:运行时检查
const data = inject('someKey')
if (!data) {
  throw new Error('数据未提供')
}

6.2 问题2:类型不匹配

错误

ts

复制代码
const user = inject('user') // 类型是 any,不安全

解决

ts

复制代码
interface User {
  name: string
  age: number
}

// 明确指定类型
const user = inject<User>('user', { name: '', age: 0 })

6.3 问题3:修改了非响应式数据

错误

ts

复制代码
// 爷爷组件
provide('staticData', { count: 0 }) // 不是响应式的!

// 孙子组件
const data = inject('staticData')
data.count++ // 修改了,但不会触发更新

解决

ts

复制代码
// 使用 ref 或 reactive 创建响应式数据
const staticData = ref({ count: 0 })
provide('staticData', staticData)

七、最佳实践

7.1 代码组织建议

ts

复制代码
// useProvide.ts - 抽离 provide 逻辑
import { ref, provide } from 'vue'

export function useProvideAppState() {
  const appState = ref({
    user: { name: '', role: '' },
    theme: 'light'
  })
  
  const updateUser = (user: User) => {
    appState.value.user = user
  }
  
  const toggleTheme = () => {
    appState.value.theme = appState.value.theme === 'light' ? 'dark' : 'light'
  }
  
  provide('appState', appState)
  provide('updateUser', updateUser)
  provide('toggleTheme', toggleTheme)
  
  return { appState, updateUser, toggleTheme }
}

7.2 在组件中使用

vue

复制代码
<script setup lang="ts">
// App.vue
import { useProvideAppState } from './useProvide'

useProvideAppState()
</script>

八、什么时候用 provide/inject?

✅ 适合的场景:

  1. 主题切换(深色/浅色模式)

  2. 用户登录信息(用户名、权限)

  3. 多语言国际化

  4. 全局配置

  5. 复杂表单(多个子组件需要访问表单数据)

❌ 不适合的场景:

  1. 父子组件通信(用 props/emit)

  2. 简单的状态共享(用 Pinia 更好)

  3. 非响应式数据(用模块导出更好)

九、总结

核心要点:

  1. 爷爷 provide,孙子 inject - 跳过中间组件

  2. 记得用 ref/reactive - 保证数据是响应式的

  3. TypeScript 类型安全 - 注入时指定类型和默认值

  4. Symbol 避免冲突 - 大型项目推荐使用

最简单的记忆:

ts

复制代码
// 提供数据
const data = ref('你好')
provide('key', data)

// 使用数据
const data = inject('key')

这样就能在任意层级的组件中共享数据了!

相关推荐
美狐美颜sdk14 分钟前
跨平台直播美颜sdk集成攻略:Android、iOS与Web的统一方案
android·前端·ios
Airser20 分钟前
npm启动Taro框架报错
前端·npm·taro
Anlici1 小时前
连载小说大学生课设 需求&架构
前端·javascript·后端
i源2 小时前
Java语言处理Js文件内容格式化
java·javascript
2501_938769992 小时前
React Server Components 进阶:数据预取与缓存
前端·react.js·缓存
“负拾捌”2 小时前
基于NodeJs实现一个MCP客户端(会话模式和无会话模式)
javascript·ai·node.js·大模型·mcp
蒜香拿铁3 小时前
Angular【基础语法】
前端·javascript·angular.js
xiaoxiao无脸男3 小时前
纯css:一个好玩的按钮边框动态动画
前端·css·css3
rookie_fly3 小时前
基于Vue的数字输入框指令
前端·vue.js·设计模式
元直数字电路验证3 小时前
ASP.NET Core Web APP(MVC)开发中无法全局配置 NuGet 包,该怎么解?
前端·javascript·ui·docker·asp.net·.net