程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

Vue 3 进阶用法:Provide / Inject 机制

balukai 2025-05-02 08:56:53 文章精选 2 ℃

一、属性下钻

属性可用于父组件向子组件传递参数,但仅限于相邻父子组件。如果一个组件要给它的子组件的子组件(即孙组件)传递参数,不得不拆成两个步骤:先传给它的儿子,再由儿子传给孙子。

尽管可以实现目的,但是中间层组件需要定义自身用不到的属性。如果嵌套层级更深,需要传递的属性更多,可以想象,这条链路的每个中间层组件都会变得不幸。这种为了透传而声明属性造成的不幸,Vue 叫它属性下钻(Prop Drilling)。

二、Provide 和 Inject

为了解决属性下钻的问题,Vue 提供了 Provide/Inject 解决方案。这个方案定义了两个角色:ProviderInjector,Provider 是数据的提供方,Injector 是数据的消费方。

Provider 通过 provide(key, data) 把数据存放在特定区域,Injector 通过 inject(key) 从特定区域获取数据。其中的 key 用于区分不同用途的数据。

这套机制跟智能取餐柜有些类似。送餐员是食物(数据)的 Provider,你是食物的 Injector,而 key 是取餐柜号。送餐员通过 provide(key, food) 把食物放到特定柜子,你通过 inject(key) 从正确的柜子取走食物并消费它。

和取餐柜不同的是,一个 Provider 可以对应多个 Injector。即父组件提供一条数据后,可以被子子孙孙组件反复消费。

provide() 函数的两个参数,第一个参数叫注入键(injection key),类型可以是字符串或 Symbol。第二个参数是注入的数据,可以是任意类型,包括响应式数据。

渲染效果:

三、同名覆盖

在一个层级较深的组件树中,如果出现多个 Provider 组件,且提供的 key 是相同的,最终 Injector 取到的数据是怎样的?

Injector 永远获取离它最近的祖先组件提供的数据,再往上的数据会被同名覆盖。用代码验证一下:

渲染效果:

如果你在开发一个很大的应用,为了防止同名覆盖,可以在 provide() 中使用 Symbol 类型代替字符串。

四、最顶级的 Provider

在 Vue 3 中,最顶级的 Provider 是 app。通过 app.provide(key, value) 提供数据。数据的消费方法没有什么不同。

五、Inject 默认值

如果子组件通过 inject(key) 消费了一个 key,但是所有的上级组件都没提供数据,会让 Vue 恼火,并发出警告信息。

为了平息 Vue 的警告信息,可以在子组件使用 inject() 时,传入第二个参数当作默认值。

这样,当所有上级都不提供数据时,子组件就会启用默认值。

有的时候,默认值需要调用工厂函数计算才能获得。此时,函数本身不是默认值,函数的返回值才是默认值,需要将 inject() 第三个参数设为 true,表示第二个参数的特殊作用。

六、处理响应式数据

如果提供的数据是响应式数据,建议把响应式数据变更数据的方法放在一个地方,打包一并提供给下游。谁声明谁治理,这样职责比较清晰。

渲染结果:

对于某些重要数据,如果上游组件不希望下游组件改动,可以先用 readonly() 方法保护好,然后再打包发送给下游。

下游修改只读数据时,Vue 不仅会阻止操作,还会发出警告:

参考资料

  • Provide / Inject,https://vuejs.org/guide/components/provide-inject.html

Tags:

最近发表
标签列表