当前位置: 首页 > news >正文

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: stringage: numberemail?: 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: stringrole: 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: stringrole: 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: stringage: 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')

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

http://www.dtcms.com/a/460721.html

相关文章:

  • 【开题答辩过程】以《基于springboot交通旅游订票系统设计与实现》为例,不会开题答辩的可以进来看看
  • 免费企业网站模板html北京网站制作设计价格
  • 网络编程(十二)epoll的两种模式
  • 某大厂跳动面试:计算机网络相关问题解析与总结
  • 服务器数据恢复—Raid5双硬盘坏,热备盘“罢工”咋恢复?
  • Vue2.0中websocket的使用-demo
  • 海外IP的适用业务范围
  • eBPF 加速时代,【深入理解计算机网络05】数据链路层:组帧,差错控制,流量控制与可靠传输的 10 Gbps 实践
  • simple websocket用法
  • 主流网络协议--助记
  • Python网络编程——UDP编程
  • 个人网站的设计流程seo资源网
  • 绿泡守护者:禁止微信更新
  • 服务端架构演进概述与核心技术概念解析
  • 美颜滤镜SDK:社交产品破局与增长的核心引擎
  • 三维模型数据结构与存储方式解析
  • 可以使用多少列创建索引?
  • 技术分享|重组单克隆抗体制备全流程:从抗体发现到纳米抗体应用,关键步骤与优势解析
  • 缝合怪deque如何综合list和vector实现及仿函数模板如何优化priority_queue实现
  • H5响应式网站示例企业网app下载
  • cmd什么命令可以知道本机到目标机的ip节点
  • C++(day6)
  • MySQL InnoDB存储引擎CheckPoint技术底层实现原理详细介绍
  • HikariCP与Spring Boot集成使用指南
  • java-代码随想录第23天|39. 组合总和、40.组合总和II、131.分割回文串
  • 【LangChain】P18 LangChain 之 Chain 深度解析(三):基于 LCEL 语法的数据库与文档处理新型 Chain
  • 2.0 Labview自定义控件中的队列引用句柄从哪拖来?
  • 【LabVIEW实用开发】--- LabVIEW调用python脚本
  • 碰一碰系统源码搭建与发视频、发文案和写好评定制化开发:支持OEM
  • 安徽建设工程信息网站简单的个人网页制作html