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')

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

相关推荐
少吃一口都不行8 小时前
脚手架学习
前端·javascript·学习
地方地方8 小时前
手写JavaScript 深拷贝
前端·javascript
yeyuningzi8 小时前
npm升级提示error engine not compatible with your version of node/npm: npm@11.6.2
前端·npm·node.js
1024小神8 小时前
next 项目中的 'use client' 是什么意思
前端
我是华为OD~HR~栗栗呀8 小时前
24届-Python面经(华为OD)
java·前端·c++·python·华为od·华为·面试
whysqwhw8 小时前
mac上AndroidStudio升级无写入权限问题
前端
wyzqhhhh8 小时前
npm相关知识
前端·npm·node.js
卢叁9 小时前
Flutter之全局路由事件监听器RouteListenerManager
前端
盗德9 小时前
为什么要用Monorepo管理前端项目?(详解)
前端·架构·代码规范