Vue开发技巧:如何用provide/inject优雅地处理插槽组件的数据流vue开发技巧:使用 provide/inj
前言
在最近的项目开发过程中遇到了一个有趣的挑战:创建一个自定义的按钮组组件,其功能类似于 ElementUI 中的 el-button-group
。这个组件允许通过插槽来动态添加按钮,形成一个统一风格的按钮组。然而,问题的关键在于,我需要从外部父组件传递参数,以便控制插槽内各个按钮的样式表现,这在 Vue.js 中并非直截了当,因为插槽内的组件实际上并不直接隶属于外层按钮组组件的子组件树。
如上图所示,我希望在外层组件通过传递一个参数来控制按钮的大小,但是通过插槽的方式是无法像父子组件一样通过props 传递数据的。最后通过查找 Vue 以及在网上查阅了一些资料,终于发现了在 Vue 中有一对 API 可以实现这个需求。
这对 API 就是 provide/inject
。
在接下来的博文中,我将详细介绍这一解决方案的实施步骤,包括如何设置 provide/inject 在插槽组件中接收和使用这些参数,以及如何确保样式的一致性和响应式设计的兼容性。通过这篇博文,我希望分享我的经验,同时也为面临相似挑战的开发者们提供一份实用的指南。
provide/inject
在 Vue.js 的组件体系中,数据流通常是遵循自上而下的模式,即父组件通过属性(props)向下传递数据给子组件,这是一种直观且直接的数据传递方式。然而,当组件层次结构变得复杂,涉及多级嵌套时,这种逐层传递数据的方式可能会导致代码冗余和难以维护。
为了解决这一问题,Vue.js 引入了 provide
和 inject
这一对高级特性。通过使用 provide
和 inject
,父组件可以为后代组件提供一个全局的上下文,这意味着无论子组件嵌套得有多深,都能够直接访问到由祖先组件提供的数据或方法,无需再进行繁琐的逐层传递。
在 Vue 官网对 provide
和 inject
的介绍很简单:
provide
提供一个值,可以被后代组件注入。
inject
注入一个由祖先组件或整个应用 (通过
app.provide()
) 提供的值
具体而言,provide
方法允许在一个组件的实例上注册一个对象,该对象包含了可以被其后代组件访问的属性和方法。而 inject
则是在一个组件中声明它想要接收哪些祖先组件提供的值。这种机制打破了常规的父子组件关系限制,使得数据和功能能够在更广泛的组件层级中自由流动,极大地提升了组件的灵活性和可复用性。
示例
定义三个组件,分别是 CompA.vue
,CompB.vue
,CompC.vue
,它们的关系为 CompA 是 CompB 的父组件,CompB 是 CompC 的父组件。
CompA.vue
<template>
<CompB></CompB>
</template>
<script setup>
import { ref, provide } from 'vue'
import CompB from './CompB.vue';
const value = ref(100)
provide('value', value)
</script>
CompB.vue
<template>
<CompC></CompC>
</template>
<script setup>
import CompC from './CompC.vue'
</script>
CompC.vue
<template>
<div>
<h2>CompC组件</h2>
CompC 组件收到 CompA 组件传递的数据是:<span style="color: red;">{{ value }}</span>
</div>
</template>
<script setup>
import { inject } from 'vue'
const value = inject('value')
</script>
在 App.vue 中 使用组件 CompA.vue
<template>
<CompA></CompA>
</template>
<script setup>
import CompA from './components/CompA.vue';
</script>
页面如图所示:
可以看到,在 CompA 组件中,我们通过 provide
来传递一个 value 数据,provide 的第一个参数代表要传递的参数名称,第二个参数为要传递的值。在 CompC 组件中,我们使用 inject 来接收传递的参数名称。这样,在 CompC 组件中即可使用 CompA 组件传递的数据,不再需要通过中间层 CompB 组件进行中转。
解决需求
知道了provide/inject的功能以及使用方法之后,下面就来解决我们的需求。
定义组件
ButtonGroup.vueButtonGroup 组件作为包裹按钮的外层组件,类似于 el-button-group 组件。
<template>
<div>
<slot></slot>
</div>
</template>
<script setup>
import { ref, defineProps, provide } from "vue";
const props = defineProps({
buttonType: String,
});
provide("buttonType", props.buttonType);
console.log(props.buttonType);
</script>
<style lang="scss"></style>
在上面的代码中,ButtonGroup.vue 组件接收一个buttonType 参数,字符串类型,可取值为 'big' | 'small',用来控制插槽内按钮的大小。在 ButtonGroup.vue 组件中引入 provide,将参数buttonType通过 provide 传递下去。
CustomButton.vue CustomButton 组件作为插槽内的单个按钮,类似于 el-button 组件。
<template>
<button class="button-single"
:class="[buttonType === 'big' ? 'button-big' : buttonType === 'small' ? 'button-small' : '']">
<slot></slot>
</button>
</template>
<script setup>
import { ref, inject } from "vue";
const buttonType = inject("buttonType");
</script>
<style lang="scss">
.button-single {
border: none;
color: #fff;
cursor: pointer;
padding: 4px 10px;
border-radius: 4px;
margin-right: 10px;
background-color: #6dbbbc;
}
.button-big {
width: 80px;
height: 80px;
font-size: 20px;
}
.button-small {
width: 60px;
height: 30px;
}
</style>
在组件中引入 inject,用来接收通过 provide 传递的buttonType。通过 buttonType的值来控制按钮的样式。
使用
在 App.vue 中引入 ButtonGroup.vue 和 CustomButton.vue 组件。
App.vue
<template>
<ButtonGroup buttonType="small">
<CustomButton style="background-color: #018001;">成功</CustomButton>
<CustomButton style="background-color: #ff0100;">失败</CustomButton>
<CustomButton style="background-color: #ffd400;">警告</CustomButton>
<CustomButton style="background-color: #8a8c8e;">提示</CustomButton>
</ButtonGroup>
<br>
<ButtonGroup buttonType="big">
<CustomButton style="background-color: #018001;">成功</CustomButton>
<CustomButton style="background-color: #ff0100;">失败</CustomButton>
<CustomButton style="background-color: #ffd400;">警告</CustomButton>
<CustomButton style="background-color: #8a8c8e;">提示</CustomButton>
</ButtonGroup>
</template>
<script setup>
import ButtonGroup from './components/ButtonGroup.vue'
import CustomButton from './components/CustomButton.vue'
</script>
效果如下图:
总结
为了解决 vue 组件数据传递层级过深的问题,vue 引入了 provide/inject API,可在父组件中使用provide给子组件传递数据,任意层级的子组件通过inject来接收数据。
provide/inject还可在使用插槽的情况下解决一些问题,因为插槽内的元素内容并不能直接视为外层组件的子组件,因此不能直接使用 props 来传递数据。不过可以使用 provide/inject将父组件的参数传递给插槽内的组件来使用。
注意:provide/inject不易过多使用,容易造成数据来源不清的问题!!!
转载自:https://juejin.cn/post/7406165066351362057