注入数据需为响应式引用并包裹进computed才能响应更新:Vue3用ref/computed提供,Vue2提供this或响应式对象;子孙组件用computed依赖inject值实现自动过滤与缓存。

注入的数据本身不是响应式源头,但可以作为 computed 的依赖参与响应式计算——关键在于“注入后不直接用,而是包裹进计算属性里”。这样既能享受 inject 的跨层级便利,又能获得 computed 的自动更新和缓存能力。
注入数据必须先转为响应式引用(Vue2/Vue3通用前提)
直接 inject 一个普通对象或原始值,它不会随源头变化而更新。要让它“活”起来,得确保提供方传的是响应式引用(ref、reactive 或包含响应式字段的对象),注入后也应保持引用关系。
- Vue3 中推荐用
ref或computed提供: // 祖先组件const searchKeyword = ref('')provide('searchKey', searchKeyword) - Vue2 中避免提供原始值,改用 data 中的响应式对象或 computed:
// 祖先组件
data() { return { keyword: '' } }provide() { return { searchKey: this.keyword } } // ❌ 错误:字符串非响应式provide() { return { searchKey: this } } // ✅ 正确:提供 this,后代可读 this.keyword
在子孙组件中用 computed 包裹 inject 值做过滤
把 inject 获取的值作为 computed 的依赖,再结合原始列表做过滤逻辑。Vue 会自动追踪这个依赖链,只要源头 keyword 变,过滤结果立刻刷新。
- Vue3 示例(
script setup):const searchKey = inject('searchKey') // ref<string></string>const productList = inject('productList') // ref<product></product>const filteredList = computed(() => {
const key = searchKey.value.trim()
if (!key) return productList.value
return productList.value.filter(p =>
p.name.includes(key) || p.desc?.includes(key)
)}) - Vue2 示例(选项式 API):
inject: ['searchKey', 'productList'],computed: {
filteredList() {
const key = (this.searchKey || '').trim()
if (!key) return this.productList || []
return (this.productList || []).filter(p =>
p.name && p.name.includes(key)
)
}}
配合 v-memo 和空搜索优化性能
当列表量大(如上千商品)时,每次 keyword 变都全量 filter 会卡顿。可在 computed 内加轻量判断,或配合 v-memo(Vue3.2+)跳过未变化子项的重渲染。
- 空关键词时直接返回原列表引用(避免无意义复制):
if (!key) return productList.value // 复用原数组引用,不 new 数组 - 多字段模糊匹配建议用
toLowerCase()统一处理,避免大小写干扰:const lowerKey = key.toLowerCase()return list.filter(p => p.name.toLowerCase().includes(lowerKey)) - 如需防抖,不建议在 computed 内做(破坏响应式),应放在 provide 方——比如祖先组件用
watch+debounce更新ref,再由 inject 拿到稳定值
实际场景:仿抖音视频流中的标签筛选
在视频卡片组件中,从顶层 VideoFeed 注入当前选中的标签(tagFilter)和全部视频列表(allVideos),用 computed 实时生成本区域可见视频:
-
顶层提供:
const tagFilter = ref('all')const allVideos = ref([])provide('tagFilter', tagFilter)provide('allVideos', allVideos) -
卡片组件中使用:
const tagFilter = inject('tagFilter')const allVideos = inject('allVideos')const visibleVideos = computed(() => {
if (tagFilter.value === 'all') return allVideos.value
return allVideos.value.filter(v => v.tags?.includes(tagFilter.value))})

















