2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vueのprovide/injectで 子コンポーネントで同一キーをprovideしたときの動きについて

Last updated at Posted at 2023-09-25

※調べたVueのバージョンは3.3.4です。

Vueでは Provide/Injectの仕組みを利用することで親コンポーネントからprovideした内容を直接の子コンポーネントだけでなく、さらに深い階層の子コンポーネントから取り出す事ができます。

provide/injectの使い方

provideは 親コンポーネントで

provide("key", "value")

または アプリケーションレベルで

import { createApp } from 'vue'

const app = createApp({})

app.provide("key", "value")

のようにして行います。

取り出すときはinjectを用いて

const value = inject("key")

のように値を取り出して利用します。

同一キーでprovideした場合どのようになるのか?

以下のようなコンポーネント階層を考えます。

image01.png

この時 Component1でprovideしたものと同一のキーをComponent3Aなどでprovideした場合、
Component4ではどのような値になるか、つまり以下の図のような場合です。

image02.png

結果としては以下のようになります。
(実験に用いたコードは最後に記載しています。image03.png

Component3A直下のComponent4のvalueAがfrom-Component3Aとなっていることから
同一キーでprovideした場合は、親からprovideした値を上書きできることがわかります。

なぜこの挙動になるのか?

ここは正確ではない可能性がありますが ソースを追いかけた感じだと

apiInject_inject を見てみるとinject関数では自信の親のprovides から取得を行っているようです。

providescreateComponentInstance で親コンポーネント、なければアプリケーションのprovidesをそのままセットしているようです。

このままだと injectしたときはルートとなる親などでprovideしたものがそのまま利用されそうですが
apiInject_provideを見てみると

    let provides = currentInstance.provides
    // by default an instance inherits its parent's provides object
    // but when it needs to provide values of its own, it creates its
    // own provides object using parent provides object as prototype.
    // this way in `inject` we can simply look up injections from direct
    // parent and let the prototype chain do the work.
    const parentProvides =
      currentInstance.parent && currentInstance.parent.provides
    if (parentProvides === provides) {
      provides = currentInstance.provides = Object.create(parentProvides)
    }
    // TS doesn't allow symbol as index type
    provides[key as string] = value

自身のprovidesと親のprovidesが同一の場合は、親のprovidesをprototypeにもつ新しいprovidesを作成し自身のprovidesとしているようです。

これがあることで自身のコンポーネント配下で疑似的なスコープを作ることができるようです。

最後に

同一キーをprovideしたときについて公式ドキュメントでは言及されていませんでしたが

ソースコードを見た限りだと特に問題なく利用することができそうです。

ただし、この挙動については公式ドキュメントに記載がないため将来的に変更される可能性があることも意識しておいたほうがよいかもしれません。

使用したコード

Componnet1

valueA, valueBをprovide

Component1.vue
<script setup>
import { provide, ref } from "vue";
import Component2 from "./Component2.vue"

const valueA = ref("from-Component1")
const valueB = ref("from-Component1")

provide("valueA", valueA)
provide("valueB", valueB)
</script>

<template>
  <div class="component1">
    <div>Component1</div>
    <Component2/>
  </div>
</template>

Componnet2

valueA, valueBをinjectして表示

Component2.vue
<script setup>
import { inject } from "vue";
import Component3A from "./Component3A.vue"
import Component3B from "./Component3B.vue"

const valueA = inject("valueA")
const valueB = inject("valueA")
</script>

<template>
  <div class="component2">
    <div>Component2</div>
    <div>
      valueA: {{ valueA }}
    </div>
    <div>
      valueB: {{ valueB }}
    </div>
    <div>
      <Component3A/>
      <Component3B/>
    </div>
  </div>
</template>

Componnet3A

valueAのみprovideする。

Component3A.vue
<script setup>
import { provide, ref } from "vue";
import Component4 from "./Component4.vue"

const valueA = ref("from-Component3A")

provide("valueA", valueA)
</script>

<template>
  <div class="component3A">
    <div>Component3A</div>
    <Component4/>
  </div>
</template>

Componnet3B

何もprovideしない

Component3B.vue
<script setup>
import Component4 from "./Component4.vue"

</script>

<template>
  <div class="component3B">
    <div>Component3B</div>
    <Component4/>
  </div>
</template>

Componnet4

valueA, valueBをinjectして表示

Component4.vue
<script setup>
import { inject } from "vue";

const valueA = inject("valueA")
const valueB = inject("valueB")
</script>

<template>
  <div class="component4">
    <div>Component4</div>
    <div>
      valueA: {{ valueA }}
    </div>
    <div>
      valueB: {{ valueB }}
    </div>
  </div>
</template>
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?