背景
最近,项目中需要实现一个按月份切换且展示多块数据、多种操作的数据大盘页面,所以我们按照数据类型,切分了多个子组件来进行开发。
而大盘中对数据的操作,需要使用当前选中的月份信息,所以我们打算将月份信息month传入数据子组件中。但是考虑到有的子组件层级相对较深,不想写太多的prop
,我们就打算使用provide/inject
来进行组件间的数据交互。
初步使用
我们可以知道,provide/inject
中绑定的数据并不是可响应的(详情见官网),不能直接传递一个数值,所以我们使用了传递一个函数来返回月份month的方法。
// 父组件
<template>
<input type="month" id="myMonth" v-model="month">
<Test />
</template>
<script>
import Test from './components/test.vue';
export default {
components: { Test },
data() {
return {
month: '2021-10',
};
},
provide() {
return {
getProvideMonth: this.getProvideMonth,
};
},
methods: {
getProvideMonth() {
return this.month;
},
},
}
</scritp>
// 子组件 components/test.vue
<template>
<span>month:{{ getProvideMonth() }}</span>
</template>
<script>
export default {
inject: ['getProvideMonth']
}
</scritp>
效果如下,子组件能随着父组件中月份信息变化而变化。
继续深入
可监听对象
官方文档中有如下描述,
如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
那么,首先我就想在data中声明一个对象来实现月份的可响应逻辑。
// 父组件
<template>
<input type="month" id="myMonth" v-model="monthObj.value">
<Test />
</template>
<script>
import Test from './components/test.vue';
export default {
components: { Test },
data() {
return {
monthObj: {
value: '2021-10',
},
};
},
provide() {
return {
monthObj: this.monthObj,
};
},
}
</scritp>
// 子组件 components/test.vue
<template>
<div>
<div>monthObj:{{ monthObj.value }}</div>
</div>
</template>
<script>
export default {
inject: ['monthObj']
}
</scritp>
这里我们将month
的信息放置在可响应的对象monthObj
中,使其数据能在子组件中获取变化后的值。
引用不变的可监听对象
这里需要注意的一个点是,这里的响应式对象需要是一个引用不变的可监听对象。 那么,在下面几种情况中,就会出现可响应失效的情况。
- 使用computed来返回对象
// 父组件
<template>
<mtd-date-picker type="month" placeholder="选择时间" value-format="yyyy-MM" v-model="month" />
<div>父组件monthObj:{{ monthObj.value }}</div>
<Test />
</template>
<script>
import Test from './components/test.vue';
export default {
components: { Test },
data() {
return {
month: '2021-10',
};
},
computed: {
monthObj() {
return {
value: this.month,
};
},
},
provide() {
return {
monthObj: this.monthObj,
};
},
}
</scritp>
// 子组件 components/test.vue
<template>
<div>
<div>monthObj:{{ monthObj.value }}</div>
</div>
</template>
<script>
export default {
inject: ['monthObj']
}
</scritp>
- 用一个对象给
monthObj
赋值
// 父组件
<template>
<mtd-date-picker type="month" placeholder="选择时间" value-format="yyyy-MM" v-model="month" />
<div>父组件monthObj:{{ monthObj.value }}</div>
<Test />
</template>
<script>
import Test from './components/test.vue';
export default {
components: { Test },
data() {
return {
month: '2021-10',
monthObj: {
value: '2021-10',
},
};
},
watch: {
month() {
this.monthObj = { value: this.month };
},
},
provide() {
return {
monthObj: this.monthObj,
};
},
}
</scritp>
// 子组件 components/test.vue
<template>
<div>
<div>monthObj:{{ monthObj.value }}</div>
</div>
</template>
<script>
export default {
inject: ['monthObj']
}
</scritp>
最终解法
上面的例子中,我们传递的是一个参数,但是实际可能传递多个参数,而多个参数会带来针对多个对应函数或者对象的维护。为了能早点下班,这里我们可以直接传入this
作为可响应的对象。
// 父组件
<template>
<mtd-date-picker type="month" placeholder="选择时间" value-format="yyyy-MM" v-model="month" />
<Test />
</template>
<script>
import Test from './components/test.vue';
export default {
components: { Test },
data() {
return {
month: '2021-10',
};
},
provide() {
return {
app: this,
};
},
}
</scritp>
// 子组件 components/test.vue
<template>
<div>
<div>子组件app:{{ app.month }}</div>
</div>
</template>
<script>
export default {
inject: ['app']
}
</scritp>
当然,有的时候我们不想暴露将父组件全部的属性给子组件,那么可以添加一个函数,返回所有需要暴露给子组件的属性(当前项目最终采用方法)。
// 父组件
<template>
<mtd-date-picker type="month" placeholder="选择时间" value-format="yyyy-MM" v-model="month" />
<Test />
</template>
<script>
import Test from './components/test.vue';
export default {
components: { Test },
data() {
return {
month: '2021-10',
};
},
provide() {
return {
getApp: this.getApp,
};
},
methods: {
getApp() {
return {
month: this.month,
// 其他需要暴露的属性
};
},
},
}
</scritp>
// 子组件 components/test.vue
<template>
<div>
<div>子组件app:{{ getApp().month }}</div>
</div>
</template>
<script>
export default {
inject: ['getApp']
}
</scritp>
总结
provide/inject
中绑定的数据并不是可响应的,若要实现响应需要传递一个可监听对象,并注意该对象的应用不能发生变化。