依存性の注入 (DI – 依存性の注入) について


依存性注入

遠い祖先コンポーネントからのデータを必要とする深い子コンポーネントがある場合はどうなるでしょうか?

  1. 図 1 に示すように、Props を使用すると、コンポーネント チェーンに沿ってステップごとにプロップを渡すことができます。
  2. 図 2 に示すように、provide を使用して祖先コンポーネントにデータを提供し、inject を使用して子孫コンポーネントにデータを注入できます。

図1:
ここに画像の説明を挿入します

図2:
ここに画像の説明を挿入します

1.提供する(提供する)

アプリケーション層でデータを提供する方法:
アプリケーション層では、app.provide() を通じて将来の世代にデータを提供できます。

//应用层提供数据
import {
    
     createApp } from 'vue'
const app = createApp({
    
     })
app.provide('message', 'hello!') // message 注入名, 'hello'

コンポーネントでの指定方法:

1.1 オプションの API では、provide オプションを通じてデータを将来の世代に提供できます
//Provide 选项提供数据【选项式】
export default {
    
    
    // 为后代组件提供数据选项
    provide: {
    
     title: '你好世界!' } // message:注入名,'hello':值
}

結合された API スクリプト設定では、provide() 関数を通じて子孫コンポーネントにデータを提供できます。

//使用 provide 函数提供数据
<script setup>
import {
    
     ref, provide } from 'vue'

const message = 'hello'
const title = ref('你好世界')
const subtitle = ref('分享经验')

function changeSubtitle(sub) {
    
    
    this.subtitle = sub
}

provide('message', message) // 提供固定数据
provide('title', title) // 提供响应式数据
provide('subtitle', subtitle) // 提供响应式数据
provide('changeSubtitle', changeSubtitle) // 为后代组件提供修改响应式数据 subtitle 的函数
</script>
1.2 コンポーネント インスタンスにアクセスしたい場合は、関数の形式で指定する必要があります (アロー関数は使用できません)。インジェクターとサプライヤー間の応答性の高いリンクを確保するために、結合されたコンポーネントの computed() 関数計算を提供するには API を使用する必要がありますが、属性はレスポンシブ データを変更するための機能も提供できます (メンテナンスを容易にするために、レスポンシブ データの変更はできるだけ同じコンポーネントに配置する必要があります)
//Provide 函数选项提供数据【选项式】
export default {
    
    
    data: () => ({
    
    
        title: '你好世界',
        subtitle: '经验'
    }),
    methods: {
    
    
        changeSubtitle(sub) {
    
    
            this.subtitle = sub
        }
    },
    // 使用函数的形式,可以访问到组件的实例 `this`
    provide() {
    
    
        return {
    
    
            // 传递数据的值为数据源 title,此方式注入方和供给方之间没有响应性链接
            title: this.title,
            // 传递数据的值为数据源 subtitle,此方式注入方和供给方之间具有响应性链接
            subtitle: computed(() => this.subtitle),
            // 为后代组件提供修改响应式数据 subtitle 的函数
            changeSubtitle: this.changeSubtitle
        }
	}
}

注: Provide オプションの計算関数を通じて提供される応答データの場合、注入によってこの計算プロパティが自動的にアンラップされるように、 app.config.unwrapInjectedRef = true を設定する必要があります。これは Vue 3.3 以降のデフォルトの動作となるため、その後のアップグレードがコードに破壊的な影響を与えることを避けるために、ここでこの設定を一時的に通知します。3.3 以降は必要なくなりました。

2.インジェクション(注射)

2.1 オプションの API では、inject オプションを使用して、祖先コンポーネントによって提供されるデータを注入する必要があることを宣言できます。これらのデータには、JS でこれを通じて直接アクセスでき、ビュー テンプレートでも直接アクセスできます。

オプション

export default {
    
    
    // 注入祖先组件提供的数据
    inject: ['message', 'title', 'subtitle', 'changeSubtitle'] 
}

組み合わせた

在这里插入代码片

**結合された API では、inject() 関数の戻り値を使用して、祖先コンポーネントによって提供されたデータを注入します。

  1. 提供されたデータの値が ref の場合、ref オブジェクトが挿入され、プロバイダーとの応答性の高い接続が維持されます。
  2. 挿入されたデータが祖先コンポーネントに提供されていない場合、警告がスローされますが、これは、provide() 関数の 2 番目のパラメーターにデフォルト値を設定することで解決できます。
  3. JS で直接アクセスしてテンプレートを表示できます**
<script setup>
import {
    
     inject } from 'vue'

const c_message = inject('message')
const title = inject('title')
const c_subtitle = inject('subtitle')
const c_changeSub = inject('changeSubtitle')
// 祖先组件并未提供 content,则会报警告,设置默认值来解决
const c_content = inject('content',  '暂时还未有内容') 
</script>
2.1 先祖コンポーネントによって提供されたデータをオブジェクトの形式で注入するために inject を使用する利点は何ですか?

a. ローカル属性名を使用して、祖先コンポーネントによって提供されたデータを注入できます (同じ場合、from オプションは省略できます)
b. 注入されたデータが祖先コンポーネントで提供されていない場合は、警告がスローされます。デフォルト値を設定するためのdefaultオプション。

オブジェクトフォームを挿入 [オプション]

export default {
    
    
    // 注入祖先组件提供的数据
    inject: {
    
    
        c_message: {
    
     
            from: 'message', // 注入的哪个数据
        },
        // 本地属性名和需要注入的数据名一致时,from 可省略
        title, // 普通数据
        c_subtitle: {
    
    from: 'subtitle'}, // 响应式数据
        c_changeSub: {
    
    from: 'changeSubtitle'}, // 修改响应式数据 subtitle 的函数
        c_content: {
    
    
            from: 'content', // 祖先组件并未提供 content,则会报警告
            default: '暂时还未有内容' // 设置默认值(可为函数等),解决警告问题
        } 
    }
}

3. 例

main.js

import {
    
     createApp } from 'vue'
import App from './App.vue'

let app = createApp(App)

// 为所有的组件提供数据
app.provide('message', '登陆成功')

// provide 选项中通过 computed 函数提供的响应式的数据,需要设置此项以保证注入会自动解包这个计算属性。
app.config.unwrapInjectedRef = true

app.mount('#app')

app.vue のオプション

<script>
import {
    
     computed } from 'vue';
import FooterVue from './components/Footer.vue'
export default {
    
    
    components: {
    
     FooterVue },
    data: () => ({
    
    
        title: '博客',
        subtitle: '百万博主分享经验'
    }),
    methods: {
    
    
        changeSubtitle(sub) {
    
    
            this.subtitle = sub
        }
    },
    // provide: {
    
     title: this.title }, // 对象方式访问不到 this
    // 如果想访问组件实例 this,需要采用函数方式
    provide() {
    
    
        return {
    
    
            title: this.title, // 和注入方不具备响应式连接
            subtitle: computed(() => this.subtitle), // 借助组合式 API 中的 computed 函数,提供和注入方具备响应式连接的数据
            changeSubtitle: this.changeSubtitle // 提供修改响应式 subtitle 的函数
        }
    }
}
</script>

<template>
    <div class="area" style="background-color: red">
        <h3>这是 APP 组件</h3>
        副标题:<input type="text" v-model="subtitle">
        <FooterVue />
    </div>
</template>

<style>
.area {
    
    
    padding: 15px;
}
</style>

app.vue の結合

<script setup>
import {
    
     provide, ref } from 'vue';
import FooterVue from './components/Footer.vue'

let title = '博客'
let subtitle = ref('百万博主分享经验')
function changeSubtitle(sub){
    
    
    subtitle.value = sub
}

// 为后代组件提供数据
provide('title', title) // 提供普通的数据
provide('subtitle', subtitle) // 提供响应式的数据,自动和注入方保持响应式的连接
provide('changeSubtitle', changeSubtitle) // 提供修改响应式的数据 subtitle 的函数
</script>

<template>
    <div class="area" style="background-color: red">
        <h3>这是 APP 组件</h3>
        副标题:<input type="text" v-model="subtitle">
        <FooterVue />
    </div>
</template>

<style>
.area {
    
    
    padding: 15px;
}
</style>

Footer.vue オプション

<script>
import DeepChildVue from './DeepChild.vue';
export default {
    
    
    components: {
    
     DeepChildVue }
}
</script>

<template>
    <div class="ar ea" style="background-color: yellow">
        <h3>这是 Footer 组件</h3>
        <DeepChildVue />
    </div>
</template>

Footer.vueの結合

<script setup>
import DeepChildVue from './DeepChild.vue';
</script>

<template>
    <div class="area" style="background-color: yellow">
        <h3>这是 Footer 组件</h3>
        <DeepChildVue />
    </div>
</template>

DeepChild.vue オプション

<script>
export default {
    
    
    // inject: ['message', 'title', 'subtitle', 'changeSubtitle'],
    // 注入祖先组件提供的数据
    inject: {
    
    
        d_message: {
    
     from: 'message' }, // 应用层数据
        title: {
    
    }, // 普通数据,如果注入属性名和本地名一致,则 from 可省略
        d_subtitle: {
    
     from: 'subtitle' }, // 响应式数据
        d_changeSub: {
    
     from: 'changeSubtitle' }, // 函数
        d_content: {
    
     from: 'content' }, // 祖先组件并未提供数据,则会抛出警告
        d_action: {
    
     from: 'action' , default: '关注博客'} // 祖先组件并未提供数据,则会抛出警告,可设置默认值,警告则取消
    },
    methods: {
    
    
        showInjectData() {
    
    
            console.log('应用层提供的数据 message 的值:' + this.d_message);
            console.log('APP 组件提供的 title 的值:' + this.title);
            console.log('APP 组件提供的 subtitle 的值:');
            console.log(this.d_subtitle);
            console.log('获取祖先组件提供 content 的数据(并没有):' + this.d_content);
            console.log('获取祖先组件提供 action 的数据(并没有,但是设置默认值):' + this.d_action);
            this.d_changeSub('EDF')
        }
    }
}
</script>


<template>
    <div class="area" style="background-color: pink">
        <h3>这是 DeepChild 组件</h3>
        <ul>
            <li>应用层提供的数据 message 的值:{
    
    {
    
     d_message }}</li>
            <li>APP 组件提供的 title 的值:{
    
    {
    
     title }}</li>
            <li>APP 组件提供的 subtitle 的值:{
    
    {
    
     d_subtitle }}</li>
            <li>获取祖先组件提供 content 的数据(并没有):{
    
    {
    
     d_content }}</li>
            <li>获取祖先组件提供 action 的数据(并没有,但是设置默认值):{
    
    {
    
     d_action }}</li>
        </ul>
        <button @click="d_changeSub('ABC')">修改APP组件中 subtitle 的值</button>
        <button @click="showInjectData">在 JS 中访问注入的数据</button>
    </div>
</template>

DeepChild.vue の結合

<script setup>
import {
    
     inject } from 'vue';

// 注入祖先组件提供的数据
let d_message = inject('message') // 应用层提供的数据
let d_title = inject('title') // 普通数据
let d_subtitle = inject('subtitle') // ref 响应式数据,则 d_subtitle 也是 ref 对象
let d_changeSub = inject('changeSubtitle') // 函数
let d_content = inject('content') // 祖先组件并未提供,则会抛出警告
let d_action = inject('action', '关注博客') // 祖先组件并未提供,则会抛出警告

function showInjectData() {
    
    
    console.log('应用层提供的数据 message 的值:' + d_message);
    console.log('APP 组件提供的 title 的值:' + d_title);
    console.log('APP 组件提供的 subtitle 的值:');
    console.log(d_subtitle);
    console.log('获取祖先组件提供 content 的数据(并没有):' + d_content);
    console.log('获取祖先组件提供 action 的数据(并没有,但是设置默认值):' + d_action);
    d_changeSub('EDF') 
}
</script>


<template>
    <div class="area" style="background-color: pink">
        <h3>这是 DeepChild 组件</h3>
        <ul>
            <li>应用层提供的数据 message 的值:{
    
    {
    
     d_message }}</li>
            <li>APP 组件提供的 title 的值:{
    
    {
    
     d_title }}</li>
            <li>APP 组件提供的 subtitle 的值:{
    
    {
    
     d_subtitle }}</li>
            <li>获取祖先组件提供 content 的数据(并没有):{
    
    {
    
     d_content }}</li>
            <li>获取祖先组件提供 action 的数据(并没有,但是设置默认值):{
    
    {
    
     d_action }}</li>
        </ul>
        <button @click="d_changeSub('ABC')">修改APP组件中 subtitle 的值</button>
        <button @click="showInjectData">在 JS 中访问注入的数据</button>
    </div>
</template>

要約する

依存性注入 (DI) は設計パターンであり、Spring フレームワークの中核概念の 1 つです。その機能は、Java クラス間の依存関係を削除し、疎結合を実現して開発とテストを容易にすることです。DI をより深く理解するには、まず DI が解決する必要がある問題を理解します。

おすすめ

転載: blog.csdn.net/www61621/article/details/129312856