提供/注入
本章节需要掌握组件基础
我们经常使用props
从父组件传递数据到子组件。相像一下你有一个多级嵌套的组件,你需要传递一个数据到底层的组件,你需要一级级往下传递,这样操作是比较繁琐的。
在这些场景下,我们使用provide
和inject
对。父组件可以注入所有子组件一个数据,而不用关心这个组件到底有多少层级结构。这个功能分成两部分共同完成:父组件使用provide
选项注入数据,而子组件有一个inject
选项可以开始使用数据。
例如我们有个嵌套组件层级像这样:
Root
└─ TodoList
├─ TodoItem
└─ TodoListFooter
├─ ClearTodosButton
└─ TodoListStatistics
如果我们想传递todo-items
长度到TodoListStatistics
,需要层级传递:TodoList
->TodoListFooter
->TodoListStatistics
。使用provide/inject,我们可以直接这么干 :
const app = Vue.createApp({})
app.component('todo-list', {
data() {
return {
todos: ['Feed a cat', 'Buy tickets']
}
},
provide: {
user: 'John Doe'
},
template: `
<div>
{{ todos.length }}
<!-- rest of the template -->
</div>
`
})
app.component('todo-list-statistics', {
inject: ['user'],
created() {
console.log(`Injected property: ${this.user}`) // > Injected property: John Doe
}
})
但是我们想传递组件实例属性时却不会工作:
app.component('todo-list', {
data() {
return {
todos: ['Feed a cat', 'Buy tickets']
}
},
provide: {
todoLength: this.todos.length // 这会报错`Cannot read property 'length' of undefined`
},
template: `
...
`
})
如果想访问组件实例属性,我们需要将provide
转换为函数并返回一个对象:
app.component('todo-list', {
data() {
return {
todos: ['Feed a cat', 'Buy tickets']
}
},
provide() {
return {
todoLength: this.todos.length
}
},
template: `
...
`
})
这可以允许我们更安全的持续开发组件,而不必担心可能会更改或移除某些子组件依赖的内容。这些组件间的接口定义是清晰的,就像props
。
事实上,你可以把这种依赖注入相像成“长props”,除了:
- 父组件并不需要知道具体是哪个层级组件使用它提供的数据。
- 子组件并不需要知道这个数据到底是谁提供来的。
响应式工作
上面这个例子,如果我们更改了todos的列表,这个改变并不会传递到提供的todsLength
属性,因为默认情况下provide
并不是响应式的。我们可以传递ref
或reactive
对象给provide
来改变这个行为。这个示例,我们响应式的改变顶级组件的属性值,我们需要为provide
的todoLength
分配一个响应式的APIcomputed
:
app.component('todo-list', {
// ...
provide() {
return {
todoLength: Vue.computed(() => this.todos.length)
}
}
})
app.component('todo-list-statistics', {
inject: ['todoLength'],
created() {
console.log(`Injected property: ${this.todoLength.value}`) // > Injected property: 5
}
})
这样,任何todos.length
的改变都会正确的反应到todoLength
的组件中。
关于computed
属性可以阅读计算属性及监听器了解更多,响应式provide/inject
参考响应式API章节。