什么是vue依赖注入?
Vue是一个用于构建用户界面的渐进式框架。
它提供了一种简单而灵活的方式来管理组件之间的数据流,即依赖注入(Dependency Injection,DI)。
依赖注入是一种设计模式,它允许一个组件从另一个组件获取它所依赖的数据或服务,而不需要自己创建或管理它们。这样可以降低组件之间的耦合度,提高代码的可维护性和可测试性。
依赖注入示意图
provide和inject
在Vue中,依赖注入的方式是通过provide和inject两个选项来实现的。
provide选项允许一个祖先组件向下提供数据或服务给它的所有后代组件。
inject选项允许一个后代组件接收来自祖先组件的数据或服务。
这两个选项都可以是一个对象或一个函数,对象的键是提供或接收的数据或服务的名称,值是对应的数据或服务。函数的返回值是一个对象,具有相同的格式。
下面是一个简单的例子,演示了如何使用依赖注入的方式共享数据:
父组件
<template>
<div>
<h1>我是祖先组件</h1>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
name: 'AncestorComponent',
components: {
ChildComponent
},
// 提供一个名为message的数据
provide: {
message: 'Hello from ancestor'
}
}
</script>
子组件
<template>
<div>
<h2>我是后代组件</h2>
<p>{{ message }}</p>
</div>
</template>
// 后代组件
<script>
export default {
name: 'ChildComponent',
// 接收一个名为message的数据
inject: ['message']
}
</script>
这样,后代组件就可以直接使用祖先组件提供的数据,而不需要通过props或事件来传递。
需要注意的是,依赖注入的数据是不可响应的,也就是说,如果祖先组件修改了提供的数据,后代组件不会自动更新。
如果需要实现响应性,可以使用一个响应式的对象或者一个返回响应式对象的函数作为provide的值。
实现响应式依赖注入的几种方式
一、提供响应式数据
方法是在提供者组件中使用ref或reactive创建响应式数据,然后通过provide提供给后代组件。后代组件通过inject接收后,就可以响应数据的变化。
提供者:
<template>
<div>
<h1>我是提供者组件</h1>
<button @click="count++">增加计数</button>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
import { ref, provide } from 'vue'
export default {
name: 'ProviderComponent',
components: {
ChildComponent
},
setup() {
// 使用ref创建一个响应式的计数器
const count = ref(0)
// 提供给后代组件
provide('count', count)
return {
count
}
}
}
</script>
接收者:
<template>
<div>
<h2>我是接收者组件</h2>
<p>计数器的值是:{{ count }}</p>
</div>
</template>
<script>
import { inject } from 'vue'
export default {
name: 'ChildComponent',
setup() {
// 接收提供者组件提供的响应式对象
const count = inject('count')
return {
count
}
}
}
</script>
二、提供修改数据的方法
提供者组件可以提供修改数据的方法函数,接收者组件调用该方法来更改数据,而不是直接修改注入的数据。
提供者:
<template>
<div>
<h1>我是提供者组件</h1>
<p>消息是:{{ message }}</p>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
import { ref, provide } from 'vue'
export default {
name: 'ProviderComponent',
components: {
ChildComponent
},
setup() {
// 使用ref创建一个响应式的消息
const message = ref('Hello')
// 定义一个更改消息的方法
function updateMessage() {
message.value = 'Bye'
}
// 提供给后代组件
provide('message', { message, updateMessage })
return {
message
}
}
}
</script>
接收者:
<template>
<div>
<h2>我是接收者组件</h2>
<p>消息是:{{ message }}</p>
<button @click="updateMessage">更改消息</button>
</div>
</template>
<script>
import { inject } from 'vue'
export default {
name: 'ChildComponent',
setup() {
// 接收提供者组件提供的响应式对象和方法
const { message, updateMessage } = inject('message')
return {
message,
updateMessage
}
}
}
</script>
三、使用readonly包装
通过readonly包装provide的数据,可以防止接收者组件修改数据,保证数据流的一致性。
提供者:
<template>
<div>
<h1>我是提供者组件</h1>
<p>姓名是:{{ name }}</p>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
import { ref, provide, readonly } from 'vue'
export default {
name: 'ProviderComponent',
components: {
ChildComponent
},
setup() {
// 使用ref创建一个响应式的姓名
const name = ref('Alice')
// 使用readonly包装提供的值,使其不可修改
provide('name', readonly(name))
return {
name
}
}
}
</script>
接收者:
<template>
<div>
<h2>我是接收者组件</h2>
<p>姓名是:{{ name }}</p>
<button @click="name = 'Bob'">尝试修改姓名</button>
</div>
</template>
<script>
import { inject } from 'vue'
export default {
name: 'ChildComponent',
setup() {
// 接收提供者组件提供的只读对象
const name = inject('name')
return {
name
}
}
}
</script>
四、使用<script setup>
在
<script setup>
组合式写法下,provide和inject默认就是响应式的,无需额外处理。
总结
依赖注入的方式共享数据在Vue中是一种高级特性,它主要用于开发插件或库,或者处理一些特殊的场景。