简介
说明
本文用示例介绍Vue中的provide和inject的用法。
官网
Provide / Inject | Vue.js
API — Vue.js
provide和inject的简介
说明
通过provide与inject,可以把父组件的数据和方法传递给其所有子孙后代。
这种传递是可以跨级的。比如:父组件可以把数据传递给孙组件。
父组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。
官方文档中有这么一句话
提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
使用场景
provide 和 inject 主要在开发高阶插件/组件库时使用,不推荐在应用程序中使用。
优点
解决了组件层级过多时,数据传递麻烦的问题
缺点
数据追踪困难:不确定数据注入层以及数据使用层。
用法
- provide
- 一个对象,或者是一个返回对象的函数。
- 对象内包含想要传递的内容,即属性和属性值
- 注意:子孙层的 provide 会覆盖 父辈 provide 中相同key的属性值
- inject
- 字符串数组,或者一个对象。
- 属性值可以为对象,包含from,default属性
- from
- 可用注入内容中的key值,即 provide 传入对象中的key
- default
- 默认值,取值不成功时候的备选。
代码执行顺序
- data
- provide
- created
- 在这个阶段$el还未生成,在这先处理privide的逻辑,子孙组件才可以取到inject值
- mounted
示例:不支持响应
provide 和 inject这种绑定不是动态响应的。即:父组件数据改变后子孙组件的数据不会改变。
代码
Parent.vue(父组件(顶层组件))
<template>
<div class="outer">
<h3>父组件</h3>
名字:<input v-model="name">
年龄:<input v-model.number="age" type="number">
<child></child>
</div>
</template>
<script>
import Child from "./Child";
export default {
name: 'Parent',
components: {Child},
data() {
return {
name: 'Tony',
age: 20,
}
},
provide() {
return {
name: this.name,
age: this.age
}
}
}
</script>
<style scoped>
.outer {
margin: 20px;
border: 2px solid red;
padding: 20px;
}
</style>
Child.vue(子组件(中间组件))
<template>
<div class="outer">
<h3>子组件</h3>
<div>获得顶层组件的name:{{ name }}</div>
<div>获得顶层组件的age:{{ age }}</div>
<grand-child></grand-child>
</div>
</template>
<script>
import GrandChild from "./GrandChild";
export default {
components: {GrandChild},
inject: ['name', 'age'],
// 详细写法
// inject:{ // 详细指定来源以及默认值
// param1:{
// from:'Parent', //表示从组件Parent传递过来的
// default:'a default msg'
// },
// reload:{
// from:'Parent'
// }
// }
}
</script>
<style scoped>
.outer {
margin: 20px;
border: 2px solid blue;
padding: 20px;
}
</style>
GrandChild.vue(孙组件(最底层组件))
<template>
<div class="outer">
<h3>孙组件</h3>
<div>获得顶层组件的name:{{ name }}</div>
<div>获得顶层组件的age:{{ age }}</div>
</div>
</template>
<script>
export default {
name: "GrandChild",
inject: ['name', 'age'],
// 详细写法
// inject:{ // 详细指定来源以及默认值
// param1:{
// from:'Parent', //表示从组件Parent传递过来的
// default:'a default msg'
// },
// reload:{
// from:'Parent'
// }
// },
}
</script>
<style scoped>
.outer {
margin: 20px;
border: 2px solid green;
padding: 20px;
}
</style>
路由(store/index.js)
import Vue from 'vue'
import Router from 'vue-router'
import Parent from "../components/Parent";
Vue.use(Router)
export default new Router({
routes: [
{
path: '/parent',
name: 'Parent',
component: Parent,
}
],
})
测试
测试1:访问
访问:http://localhost:8080/#/parent
可以看到:子组件和孙组件都能获得父组件的值。
测试2:修改父组件数据
示例:支持响应
provide 和 inject这种绑定不是动态响应的。
实现动态响应的方案是:将一个函数赋值给provide的一个值,这个函数返回父组件的动态数据,然后在子孙组件里面调用这个函数。
代码
Parent.vue(父组件(顶层组件))
<template>
<div class="outer">
<h3>父组件</h3>
名字:<input v-model="name">
年龄:<input v-model.number="age" type="number">
<child></child>
</div>
</template>
<script>
import Child from "./Child";
export default {
name: 'Parent',
components: {Child},
data() {
return {
name: 'Tony',
age: 20,
}
},
provide() {
return {
name: this.name,
age: () => this.age //这里传入的是一个function
}
}
}
</script>
<style scoped>
.outer {
margin: 20px;
border: 2px solid red;
padding: 20px;
}
</style>
Child.vue(子组件(中间组件))
<template>
<div class="outer">
<h3>子组件</h3>
<div>获得顶层组件的name:{{ name }}</div>
<!--这里改为了使用函数来获得值-->
<div>获得顶层组件的age:{{ age() }}</div>
<grand-child></grand-child>
</div>
</template>
<script>
import GrandChild from "./GrandChild";
export default {
components: {GrandChild},
inject: ['name', 'age'],
// 详细写法
// inject:{ // 详细指定来源以及默认值
// param1:{
// from:'Parent', //表示从组件Parent传递过来的
// default:'a default msg'
// },
// reload:{
// from:'Parent'
// }
// },
}
</script>
<style scoped>
.outer {
margin: 20px;
border: 2px solid blue;
padding: 20px;
}
</style>
GrandChild.vue(孙组件(最底层组件))
<template>
<div class="outer">
<h3>孙组件</h3>
<div>获得顶层组件的name:{{ name }}</div>
<!--这里改为了使用函数来获得值-->
<div>获得顶层组件的age:{{ age() }}</div>
</div>
</template>
<script>
export default {
name: "GrandChild",
inject: ['name', 'age'],
// 详细写法
// inject:{ // 详细指定来源以及默认值
// param1:{
// from:'Parent', //表示从组件Parent传递过来的
// default:'a default msg'
// },
// reload:{
// from:'Parent'
// }
// }
}
</script>
<style scoped>
.outer {
margin: 20px;
border: 2px solid green;
padding: 20px;
}
</style>
路由(store/index.js)
import Vue from 'vue'
import Router from 'vue-router'
import Parent from "../components/Parent";
Vue.use(Router)
export default new Router({
routes: [
{
path: '/parent',
name: 'Parent',
component: Parent,
}
],
})
测试
测试1:访问
访问:http://localhost:8080/#/parent
测试2:修改父组件数据
可以看到:
- 名字(name)没有动态更新。因为没用函数
- 年龄(age)动态更新。因为用了函数