vue 中的依赖注入,官网文档已经非常详细,笔者在这里总结一份
目录
1、背景介绍
2、代码实现
2.1、依赖注入固定值
2.2、 依赖注入响应式数据
3、注入别名
4、注入默认值
5、应用层 Provide
6、使用 Symbol 作注入名
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>
运行效果
至此完