vue 依赖注入使用教程

vue 中的依赖注入,官网文档已经非常详细,笔者在这里总结一份

目录

1、背景介绍

2、代码实现

2.1、依赖注入固定值

2.2、 依赖注入响应式数据

3、注入别名

4、注入默认值

5、应用层 Provide

6、使用 Symbol 作注入名

7、组合式API使用依赖注入


1、背景介绍

为什么会出现依赖注入呢?其实它是要解决 Prop 逐级透传问题

Prop 逐级透传问题,即通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props,如果只是父传子,这样层级不深的结构,是比较简单的;如果是层级非常深,祖太爷、太爷、爷、父、子、孙、重孙组件......,这样多层级嵌套的组件,形成了一颗巨大的组件树,此时,如果重孙组件需要祖太爷组件中的部分数据,在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦。而且中间的爷、父等组件可能根本不关心这些 props,为了使重孙组件能够访问到它们,仍然需要定义并向下传递。如果组件链路非常长,可能会影响到更多这条路上的组件。这一问题被称为“prop 逐级透传”

vue 中提供 provide 和 inject 可以帮助我们解决这一问题,一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖

下图摘自vue官网

2、代码实现

下面代码实现依赖注入

新建vue项目,这里笔者定义3个组件。图书馆组件Library(爷),图书组件Book(父)、标题组件Title(孙),实现图书馆组件Library(爷)提供数据,标题组件Title(孙)注入使用

项目结构

2.1、依赖注入固定值

图书馆组件Library(爷)提供 bookName

<template>
    <div>
        <h1>图书馆</h1>
        <Book/>
    </div>
</template>
<script>
import Book from './Book.vue'
export default {
    components: {
        Book
    },
    provide: {
        bookName: '三国演义'
    },
}
</script>

图书组件Book(父)

<template>
    <div>
        <h2>书名</h2>
        <Title />
    </div>
</template>
<script>
import Title from './Title.vue'
export default {
   components: {
    Title
   }
}
</script>

标题组件Title(孙),注入bookName 

<template>
    <div>
        <p>标题</p>
        <p>{
   
   {bookName}}</p>
    </div>
</template>
<script>
export default {
    inject: ['bookName'],
}
</script>

App.vue,引入图书馆组件Library

<template>
  <Library />
</template>

<script>
import Library from './components/Library.vue'

export default {
  name: 'App',
  components: {
    Library
  }
}
</script>

运行效果

2.2、 依赖注入响应式数据

上例2.1依赖注入的 bookName 是个固定的值,本例将 bookName 改成响应式数据

图书组件Book(父)和标题组件Title(孙)不需要改,只改图书馆组件Library(爷)即可

图书馆组件Library(爷)

为保证注入方和供给方之间的响应性链接,需要使用 computed() 函数提供一个计算属性

<template>
    <div>
        <h1>图书馆</h1>
        <button @click="changeBookName">修改书籍名称</button>
        <Book/>
    </div>
</template>
<script>
import { computed } from 'vue'
import Book from './Book.vue'
export default {
    components: {
        Book
    },
    provide() {
        return {
            bookName: computed(()=>this.bookName)
        }
    },
    data() {
        return {
            bookName: '三国演义'
        }
    },
    methods: {
        changeBookName() {
            this.bookName = '西游记'
        }
    }
    
}
</script>

图书组件Book(父)

<template>
    <div>
        <h2>书名</h2>
        <Title />
    </div>
</template>
<script>
import Title from './Title.vue'
export default {
   components: {
    Title
   }
}
</script>

标题组件Title(孙)

<template>
    <div>
        <p>标题</p>
        <p>{
   
   {bookName}}</p>
    </div>
</template>
<script>
export default {
    inject: ['bookName'],
}
</script>

运行效果

点击按钮,修改图书名称

3、注入别名

上面例子中标题组件Title(孙)中注入的 bookName,使用是也是bookName,即访问的本地属性名和注入名是相同的;如果我们想要用一个不同的本地属性名注入该属性,我们需要在 inject 选项的属性上使用对象的形式

看下面代码

注入bookName,本地属性使用 name

<template>
    <div>
        <p>标题</p>
        <p>{
   
   {name}}</p>
    </div>
</template>
<script>
export default {
    inject: {
        name: {
            from: 'bookName'
        }
    }
}
</script>

4、注入默认值

如果注入的属性没有任何组件提供,则会抛出一个运行时警告,如果注入一个值时不要求必须有提供者,那么我们应该声明一个默认值,和 props 类似

将图书馆组件Library(爷)中的provide注释掉

<template>
    <div>
        <h1>图书馆</h1>
        <button @click="changeBookName">修改书籍名称</button>
        <Book/>
    </div>
</template>
<script>
import { computed } from 'vue'
import Book from './Book.vue'
export default {
    components: {
        Book
    },
    // provide() {
    //     return {
    //         bookName: computed(()=>this.bookName)
    //     }
    // },
    data() {
        return {
            bookName: '三国演义'
        }
    },
    methods: {
        changeBookName() {
            this.bookName = '西游记'
        }
    }
    
}
</script>

图书组件Book(父)不变

<template>
    <div>
        <h2>书名</h2>
        <Title />
    </div>
</template>
<script>
import Title from './Title.vue'
export default {
   components: {
    Title
   }
}
</script>

标题组件Title(孙)中添加注入的默认值

<template>
    <div>
        <p>标题</p>
        <p>{
   
   {name}}</p>
    </div>
</template>
<script>
export default {
    inject: {
        name: {
            from: 'bookName',
            default: '红楼梦'
        }
    }
}
</script>

运行效果

5、应用层 Provide

除了在一个组件中提供依赖,我们还可以在整个应用层面提供依赖

在 main.js 中提供依赖

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

const app = createApp(App)
app.provide('title', '古典名著')
app.config.unwrapInjectedRef = true
app.mount('#app')

在标题组件Title(孙)中注入title

<template>
    <div>
        <p>标题</p>
        <p>{
   
   { title }}</p>
        <p>{
   
   {name}}</p>
    </div>
</template>
<script>
export default {
    inject: {
        name: {
            from: 'bookName',
            default: '红楼梦'
        },
        title: {}
    }
}
</script>

运行效果

6、使用 Symbol 作注入名

如果有非常多的依赖提供,建议最好使用 Symbol 来作为注入名以避免潜在的冲突

笔者在components目录下定义keys.js文件

keys.js 文件内容

export const bookName = Symbol()

在图书馆组件Library(爷)中引入keys.js

<template>
    <div>
        <h1>图书馆</h1>
        <button @click="changeBookName">修改书籍名称</button>
        <Book/>
    </div>
</template>
<script>
import { computed } from 'vue'
import Book from './Book.vue'
import { bookName } from './keys.js'
export default {
    components: {
        Book
    },
    provide() {
        return {
            [bookName]: computed(()=>this.bookName)
        }
    },
    data() {
        return {
            bookName: '三国演义'
        }
    },
    methods: {
        changeBookName() {
            this.bookName = '西游记'
        }
    }
    
}
</script>

在标题组件Title(孙)中引入keys.js

<template>
    <div>
        <p>标题</p>
        <p>{
   
   { title }}</p>
        <p>{
   
   {name}}</p>
    </div>
</template>
<script>
import { bookName } from './keys.js'
export default {
    inject: {
        name: {
            from: bookName,
            default: '红楼梦'
        },
        title: {}
    }
}
</script>

运行效果

7、组合式API使用依赖注入

还是上面的例子,main.js 中提供全局依赖 title

main.js内容

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

const app = createApp(App)
app.provide('title', '古典名著')
app.config.unwrapInjectedRef = true
app.mount('#app')

keys.js 文件内容

export const bookName = Symbol()

图书馆组件Library(爷)

<template>
    <div>
        <h1>图书馆</h1>
        <button @click="changeBookName">修改书籍名称</button>
        <Book/>
    </div>
</template>
<script setup>
import { ref , provide } from 'vue'
import Book from './Book.vue'
import { bookName } from './keys.js'

const name = ref('水浒传')
const changeBookName = ()=>{
    name.value = '西游记'
}
provide(bookName, name)
</script>

图书组件Book(父)

<template>
    <div>
        <h2>书名</h2>
        <Title />
    </div>
</template>
<script setup>
import Title from './Title.vue'
</script>

标题组件Title(孙)

<template>
    <div>
        <p>标题</p>
        <p>{
   
   { title }}</p>
        <p>{
   
   {name}}</p>
    </div>
</template>
<script setup>
import { bookName } from './keys.js'
import { inject } from 'vue'
const name = inject(bookName, '这是默认值红楼梦')
const title = inject('title')
</script>

运行效果

如果想在注入的组件内修改注入的值,可以在依赖的地方传入修改依赖值的方法

图书馆组件Library(爷)中添加修改依赖值的方法

<template>
    <div>
        <h1>图书馆</h1>
        <button @click="changeBookName">修改书籍名称</button>
        <Book/>
    </div>
</template>
<script setup>
import { ref , provide } from 'vue'
import Book from './Book.vue'
import { bookName } from './keys.js'

const name = ref('水浒传')
const changeBookName = ()=>{
    name.value = '西游记'
}
const updateBookName = ()=> {
    name.value = '道德经'
}
provide(bookName, {name, updateBookName})
</script>

标题组件Title(孙)中调用方法

<template>
    <div>
        <p>标题</p>
        <p>{
   
   { title }}</p>
        <p>{
   
   {name}}</p>
        <button @click="updateBookName">子组件调用注入的方法修改注入值</button>
    </div>
</template>
<script setup>
import { bookName } from './keys.js'
import { inject } from 'vue'
const {name, updateBookName} = inject(bookName)
const title = inject('title')
</script>

运行效果

至此完

猜你喜欢

转载自blog.csdn.net/wsjzzcbq/article/details/129256187
今日推荐