Vue3 グローバル コンポーネント通信によるソース コード分析の提供/挿入

WBOY
リリース: 2023-05-14 17:58:06
転載
1134 人が閲覧しました

1. はじめに

名前が示すように、祖父と孫のコンポーネントには、親子コンポーネント (「世代分離コンポーネント」とも呼ばれます) 間の通信よりも深い参照関係があります。 ##C コンポーネントをコンポーネント B に導入します コンポーネント B をコンポーネント A に導入してレンダリングします このとき、A は C のおじいちゃんレベルです (もっと階層関係があるかもしれません) props を使用する場合は、渡すことしかできませんレベルごとに行うのは面倒すぎるため、より直接的なコミュニケーション方法が必要です。

両者の関係は次のとおりです。Grandson.vue は Grandfather.vue の下に直接マウントされていません。それらの間には少なくとも 1 つの Son.vue があります (複数ある場合があります):

Grandfather.vue
└─Son.vue
  └─Grandson.vue
ログイン後にコピー

祖父と孫のコンポーネントコミュニケーションのスキームは、上司と部下の関係が一貫しているため、親子コンポーネントのコミュニケーションにも適用でき、祖父と孫の関係を父と息子の関係に置き換えるだけで済みます。

2. Provide / inject

この機能には 2 つの部分があります: Grandfather.vue にはデータを提供する Provide オプションがあり、Grandson.vue にはデータの使用を開始する Inject オプションがあります。 . .

    Grandfather.vue は、provide を通じて Grandson.vue に値を渡します (定義された関数を含めることができます)
  • Grandson.vue は、inject を通じて Grandfather に値を渡します.vue は、おじいちゃんコンポーネントのイベント実行をトリガーします
  • ##コンポーネント階層がどれほど深くても、provide を開始するコンポーネントは、そのすべての下位コンポーネントの依存関係プロバイダーとして使用できます

    この部分の内容 変更点は非常に大きくなっていますが、使い方はとても簡単です。同じ箇所もあるので慌てないでください:


親コンポーネントは、どの子コンポーネントがそのプロパティが提供するプロパティを使用するかを知る必要があります

  • サブコンポーネントは、inject プロパティの出所を知る必要はありません

  • #さらに、覚えておいていただきたいのは、provide バインディングと inject バインディングは応答しないということです。これは意図的なものですが、リッスン可能なオブジェクトが渡された場合でも、オブジェクトのプロパティは引き続き応答します。

    3.provide の開始
最初に 2.x の使用法を確認しましょう:

export default {
  // 定义好数据
  data () {
    return {
      tags: [ '中餐', '粤菜', '烧腊' ]
    }
  },
  // provide出去
  provide () {
    return {
      tags: this.tags
    }
  }
}
ログイン後にコピー

provide の古いバージョンの使用法はデータと似ており、どちらもリターンとして構成されています。オブジェクト関数。

3.x の新しいバージョンの Provide は、使用方法が 2.x とは大きく異なります。

3.x では、provide はセットアップでインポートして有効にする必要があり、まったく新しいメソッドになりました。

データを提供するたびに、それを個別に呼び出す必要があります。

呼び出しのたびに、2 つのパラメータを渡す必要があります:



Parameters

TypeDescriptionkeystringデータの名前データの値プロバイドの作成方法を見てみましょう: 操作は非常に簡単ですが、そうする必要があります。 Provide はレスポンス スタイルではないことに注意してください。レスポンシブにしたい場合は、レスポンシブ データ 4、receive inject
##valueany
// 记得导入provide
import { defineComponent, provide } from 'vue'

export default defineComponent({
  // ...
  setup () {
    // 定义好数据
    const msg: string = 'Hello World!';

    // provide出去
    provide('msg', msg);
  }
})
ログイン後にコピー

を渡す必要があります。まず 2.x の使用法を確認してみましょう。

export default {
  inject: ['tags'],
  mounted () {
    console.log(this.tags);
  }
}
ログイン後にコピー

古いバージョンの inject の使用法は props の使用法と似ていますが、新しいバージョンの 3.x inject の使用法も 2.x の使用法とは大きく異なります。

3.x では、inject は Provide と同じです。また、最初にインポートしてからセットアップで有効にする必要があります。これはまったく新しいメソッドでもあります。

データを挿入するたびに、それを個別に呼び出す必要があります。

呼び出しのたびに、渡す必要があるパラメータは 1 つだけです:


Parameter

TypeDescription#keystringprovided に対応するデータ名
// 记得导入inject
import { defineComponent, inject } from 'vue'

export default defineComponent({
  // ...
  setup () {
    const msg: string = inject('msg') || '';
  }
})
ログイン後にコピー

也是很简单(写 TS 的话,由于 inject 到的值可能是 undefined,所以要么加个 undefined 类型,要么给变量设置一个空的默认值)。

5、响应性数据的传递与接收

之所以要单独拿出来说, 是因为变化真的很大

在前面我们已经知道,provide 和 inject 本身不可响应,但是并非完全不能够拿到响应的结果,只需要我们传入的数据具备响应性,它依然能够提供响应支持。

我们以 ref 和 reactive 为例,来看看应该怎么发起 provide 和接收 inject。

先在 Grandfather.vue 里 provide 数据:

export default defineComponent({
  // ...
  setup () {
    // provide一个ref
    const msg = ref<string>(&#39;Hello World!&#39;);
    provide(&#39;msg&#39;, msg);

    // provide一个reactive
    const userInfo: Member = reactive({
      id: 1,
      name: &#39;Petter&#39;
    });
    provide(&#39;userInfo&#39;, userInfo);

    // 2s 后更新数据
    setTimeout(() => {
      // 修改消息内容
      msg.value = &#39;Hi World!&#39;;

      // 修改用户名
      userInfo.name = &#39;Tom&#39;;
    }, 2000);
  }
})
ログイン後にコピー

在 Grandsun.vue 里 inject 拿到数据:

export default defineComponent({
  setup () {
    // 获取数据
    const msg = inject(&#39;msg&#39;);
    const userInfo = inject(&#39;userInfo&#39;);

    // 打印刚刚拿到的数据
    console.log(msg);
    console.log(userInfo);

    // 因为 2s 后数据会变,我们 3s 后再看下,可以争取拿到新的数据
    setTimeout(() => {
      console.log(msg);
      console.log(userInfo);
    }, 3000);

    // 响应式数据还可以直接给 template 使用,会实时更新
    return {
      msg,
      userInfo
    }
  }
})
ログイン後にコピー

非常简单,非常方便!!!

响应式的数据 provide 出去,在子孙组件拿到的也是响应式的,并且可以如同自身定义的响应式变量一样,直接 return 给 template 使用,一旦数据有变化,视图也会立即更新。

但上面这句话有效的前提是,不破坏数据的响应性,比如 ref 变量,你需要完整的传入,而不能只传入它的 value,对于 reactive 也是同理,不能直接解构去破坏原本的响应性

切记!切记!!!

6、引用类型的传递与接收 (针对非响应性数据的处理)

provide 和 inject 并不是可响应的,这是官方的故意设计,但是由于引用类型的特殊性,在子孙组件拿到了数据之后,他们的属性还是可以正常的响应变化。

先在 Grandfather.vue 里 provide 数据:

export default defineComponent({
  // ...
  setup () {
    // provide 一个数组
    const tags: string[] = [ &#39;中餐&#39;, &#39;粤菜&#39;, &#39;烧腊&#39; ];
    provide(&#39;tags&#39;, tags);

    // provide 一个对象
    const userInfo: Member = {
      id: 1,
      name: &#39;Petter&#39;
    };
    provide(&#39;userInfo&#39;, userInfo);

    // 2s 后更新数据
    setTimeout(() => {
      // 增加tags的长度
      tags.push(&#39;叉烧&#39;);

      // 修改userInfo的属性值
      userInfo.name = &#39;Tom&#39;;
    }, 2000);
  }
})
ログイン後にコピー

在 Grandsun.vue 里 inject 拿到数据:

export default defineComponent({
  setup () {
    // 获取数据
    const tags: string[] = inject(&#39;tags&#39;) || [];
    const userInfo: Member = inject(&#39;userInfo&#39;) || {
      id: 0,
      name: &#39;&#39;
    };

    // 打印刚刚拿到的数据
    console.log(tags);
    console.log(tags.length);
    console.log(userInfo);

    // 因为 2s 后数据会变,我们 3s 后再看下,能够看到已经是更新后的数据了
    setTimeout(() => {
      console.log(tags);
      console.log(tags.length);
      console.log(userInfo);
    }, 3000);
  }
})

export default defineComponent({
  setup () {
    // 获取数据
    const tags: string[] = inject(&#39;tags&#39;) || [];
    const userInfo: Member = inject(&#39;userInfo&#39;) || {
      id: 0,
      name: &#39;&#39;
    };

    // 打印刚刚拿到的数据
    console.log(tags);
    console.log(tags.length);
    console.log(userInfo);

    // 因为 2s 后数据会变,我们 3s 后再看下,能够看到已经是更新后的数据了
    setTimeout(() => {
      console.log(tags);
      console.log(tags.length);
      console.log(userInfo);
    }, 3000);
  }
})
ログイン後にコピー

引用类型的数据,拿到后可以直接用,属性的值更新后,子孙组件也会被更新。
但是!!!由于不具备真正的响应性,return 给模板使用依然不会更新视图,如果涉及到视图的数据,请依然使用 响应式 API 。

7、基本类型的传递与接收 (针对非响应性数据的处理)

基本数据类型被直接 provide 出去后,再怎么修改,都无法更新下去,子孙组件拿到的永远是第一次的那个值。

先在 Grandfather.vue 里 provide 数据:

export default defineComponent({
  // ...
  setup () {
    // provide 一个数组的长度
    const tags: string[] = [ &#39;中餐&#39;, &#39;粤菜&#39;, &#39;烧腊&#39; ];
    provide(&#39;tagsCount&#39;, tags.length);

    // provide 一个字符串
    let name: string = &#39;Petter&#39;;
    provide(&#39;name&#39;, name);

    // 2s 后更新数据
    setTimeout(() => {
      // tagsCount 在 Grandson 那边依然是 3
      tags.push(&#39;叉烧&#39;);

      // name 在 Grandson 那边依然是 Petter
      name = &#39;Tom&#39;;
    }, 2000);
  }
})
ログイン後にコピー

在 Grandsun.vue 里 inject 拿到数据:

export default defineComponent({
  setup () {
    // 获取数据
    const name: string = inject(&#39;name&#39;) || &#39;&#39;;
    const tagsCount: number = inject(&#39;tagsCount&#39;) || 0;

    // 打印刚刚拿到的数据
    console.log(name);
    console.log(tagsCount);

    // 因为 2s 后数据会变,我们 3s 后再看下
    setTimeout(() => {
      // 依然是 Petter
      console.log(name);

      // 依然是 3
      console.log(tagsCount);
    }, 3000);
  }
})
ログイン後にコピー

很失望,并没有变化。


那么是否一定要定义成响应式数据或者引用类型数据呢?

当然不是,我们在 provide 的时候,也可以稍作修改,让它能够同步更新下去。

先在 Grandfather.vue 里 provide 数据:

export default defineComponent({
  // ...
  setup () {
    // provide 一个数组的长度
    const tags: string[] = [ &#39;中餐&#39;, &#39;粤菜&#39;, &#39;烧腊&#39; ];
    provide(&#39;tagsCount&#39;, (): number => {
      return tags.length;
    });

    // provide 字符串
    let name: string = &#39;Petter&#39;;
    provide(&#39;name&#39;, (): string => {
      return name;
    });

    // 2s 后更新数据
    setTimeout(() => {
      // tagsCount 现在可以正常拿到 4 了
      tags.push(&#39;叉烧&#39;);

      // name 现在可以正常拿到 Tom 了
      name = &#39;Tom&#39;;
    }, 2000);
  }
})
ログイン後にコピー

再来 Grandsun.vue 里修改一下 inject 的方式,看看这次拿到的数据:

export default defineComponent({
  setup () {
    // 获取数据
    const tagsCount: any = inject(&#39;tagsCount&#39;);
    const name: any = inject(&#39;name&#39;);

    // 打印刚刚拿到的数据
    console.log(tagsCount());
    console.log(name());

    // 因为 2s 后数据会变,我们 3s 后再看下
    setTimeout(() => {
      // 现在可以正确得到 4
      console.log(tagsCount());

      // 现在可以正确得到 Tom
      console.log(name());
    }, 3000);
  }
})
ログイン後にコピー

这次可以正确拿到数据了,看出这2次的写法有什么区别了吗?

基本数据类型,需要 provide 一个函数,将其 return 出去给子孙组件用,这样子孙组件每次拿到的数据才会是新的。
但由于不具备响应性,所以子孙组件每次都需要重新通过执行 inject 得到的函数才能拿到最新的数据。

按我个人习惯来说,使用起来挺别扭的,能不用就不用……

由于不具备真正的响应性,return 给模板使用依然不会更新视图,如果涉及到视图的数据,请依然使用 响应式 API 。

以上がVue3 グローバル コンポーネント通信によるソース コード分析の提供/挿入の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート