Vue2 写法
用于祖先组件给后代组件传递数据:
① 在祖先组件中配置 provide
,指定需要传递的数据(provide
用法类比 data
)
② 在后代组件中配置 inject
,并接收数据(inject
用法类比 props
)
provide
有两种写法:
对象式写法:只能传递静态信息,不能获取this
函数式写法:可以获取并传递this
的信息,this
指向当前组件实例provide
会被代理成_provide
,我们可以通过this._provide.属性名
在父组件中获取provide
对象的属性- 注意:在后代组件中,不建议修改祖先组件传递下来的数据
<template>
<div>
<button @click="showPro">点击获取 provide 的属性信息</button>
<hr />
<Son />
</div>
</template>
<script>
import Son from './components/Son.vue';
export default {
name: 'App',
components: { Son },
data() {
return { msg: 'app 组件的数据' };
},
/* 配置 provide */
// 函数式写法,可以通过 this 访问当前组件
provide() {
// 设置需要传递的数据
return { childMsg: this.msg };
},
// 对象式写法,只能传递静态数据
// provide: { childMsg: "静态数据" },
methods: {
showPro() {
// 我们可以通过 this._provided 获取 provide 的对象数据
console.log('provide 对象数据', this._provided);
},
},
};
</script>
<template>
<div class="son">
<p>inject:{{ childMsg }}</p>
</div>
</template>
<script>
export default {
name: 'Son',
inject: ['childMsg'], // 配置 inject、 接收祖先组件传递的数据
};
</script>
Vue3 写法
- 为后代组件提供数据:
provide(key, value)
key
可以为 String / Symbol ;value
可以为任意类型,包括响应式的状态- 提供的响应式状态使后代组件可以由此和提供者建立响应式的联系
- 注入上层组件提供的数据:
inject(key[, 默认值])
- 如果提供的值是一个 ref,注入进来的会是该 ref 对象,而不会自动解包为其内部的值。这使得注入方能够通过 ref 对象保持和供给方的响应式链接
- 在一些场景中,默认值可能需要通过调用一个函数或初始化一个类来获取。为避免在用不到默认值的情况下进行不必要的计算或产生副作用,我们可以使用工厂函数来创建默认值,eg:
inject(key, () => new ExpensiveClass())
<template>
<HelloWorld />
<button @click="textDemo += '~'">update</button>
</template>
<script lang="ts" setup>
import { ref, provide } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue';
const textDemo = ref('Vue.js');
provide('title', textDemo);
</script>
<template>
{{ val }}
</template>
<script lang="ts" setup>
import { inject } from 'vue';
const val = inject('title', '默认值');
</script>
和响应式数据配合使用
当 provide / inject 响应式的数据时,建议尽可能将任何对响应式状态的变更都保持在供给方组件中。这样可以确保所提供状态的声明和变更操作都内聚在同一个组件内,使其更容易维护
有的时候,我们可能需要在注入方组件中更改数据。在这种情况下,我们推荐在供给方组件内声明并提供一个更改数据的方法函数:
<template>
<HelloWorld />
<button @click="textDemo += '~'">update</button>
</template>
<script lang="ts" setup>
import { ref, provide } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue';
const textDemo = ref('Vue.js');
const updateTitle = (val: string) => {
textDemo.value += val;
};
provide('title', { textDemo, updateTitle }); // 提供依赖及更新依赖的方法
</script>
<template>
<button @click="updateTitle('+')">update</button>
{{ textDemo }}
</template>
<script lang="ts" setup>
import { inject } from 'vue';
const { textDemo, updateTitle } = inject<any>('title'); // 注入依赖及更新依赖的方法
</script>
- 如果想确保提供的数据不能被注入方的组件更改,可以使用
readonly()
来包装提供的值
eg:provide('read-only-count', readonly(count))
readonly()
接收一个对象 ( 不论是响应式还是普通 ) ,返回一个原值的只读代理
使用 Symbol 作注入名
推荐在一个单独的文件中导出这些 Symbol 注入名
// keys.js
import type { InjectionKey } from 'vue';
export const myInjectionKey = Symbol() as InjectionKey<string>;
// 在供给方组件中
import { provide } from 'vue';
import { myInjectionKey } from './keys.js';
provide(myInjectionKey, {
/* 要提供的数据 */
});
// 在注入方组件中
import { inject } from 'vue';
import { myInjectionKey } from './keys.js';
const injected = inject(myInjectionKey);
应用层 Provide
在应用级别提供的数据在该应用内的所有组件中都可以注入
import { createApp } from 'vue';
const app = createApp({});
app.provide('message', 'hello!');