Vue provide跨層級資料傳遞

通常,當我們需要將數據從父組件傳遞給子組件時,我們會使用props,但當是一個大型組件樹時,要將父組件資料傳遞給比較深層的組件時,如果僅使用 props,我們將不得不在整個父鏈中傳遞相同的 props

所以我們可以使用Provide(提供) / Inject(注入)來解決問題

假設我們有一個三層的組件樹,要將資料從父組件傳遞至user組件,使用Provide傳遞資料

Provide

Options API

非響應式:

export default {
provide:{
user:{
name: '小明',
uuid: 78163
}
},
}

響應式:

export default {
data() {
return {
user: {
name: '小明',
uuid: 78163
}
}
},
provide(){
return {
user: this.user
}
},
}

Composition API

非響應式:

<script setup>
import { ref, provide } from 'vue'

// provide static value
provide('foo', 'bar')
</script>

響應式:

<script setup>
import { ref, provide } from 'vue'

// provide reactive value
const count = ref(0)
provide('count', count)
</script>

Inject

Options API

再使用Inject注入

export default {
inject: ['user'],
created() {
console.log(this.user) // injected value
}
}

Composition API

<script setup>
import { inject } from 'vue'

// inject static value with default
const foo = inject('foo')

// inject reactive value
const count = inject('count')

</script>

注入資料更改名稱

export default {
inject: {
/* local key */ localMessage: {
from: /* injection key */ 'user'
}
},
created() {
console.log(this.user); // injected value
}
}

更改

範例

Options API

非響應式範例:

以下範例更改inject接收的資料,會發現資料不是響應式的,在card元件中的名子還是父組件傳遞的資料

<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.global.js"></script>
</head>
<body class="bg-lighter">

<div id="app">
<card></card>
</div>

<script>
const user = {
template: `<div>
user 元件:<br />
姓名: {{ user.name }}, <br />
uuid: {{ user.uuid }}
</div>`,
inject: ['user'],
created() {
console.log(this.user);

// 如果根元件沒有使用 function return 則不能使用響應式
this.user.name = '小美';
}
}

const app = Vue.createApp({
data() {
return {
user: {
name: '小明',
uuid: 78163
}
}
},
provide:{
user:{
name: '小明',
uuid: 78163
}
},
})

app.component('card', {
data() {
return {
title: '文件標題',
content: '文件內文',
toggle: false
}
},
components: {
user
},
inject: ['user'],
template: `
<div class="card" style="width: 18rem;">
<div className="card-header">card 元件,姓名{{ user.name }}</div>
<div class="card-body">
<user></user>
</div>
</div>
`,
});

app.mount('#app');
</script>
</body>
</html>

響應式範例:

可以看到更改的資料都被調整

<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.global.js"></script>
</head>
<body class="bg-lighter">

<div id="app">
<card></card>
</div>

<script>
const user = {
template: `<div>
user 元件:<br />
姓名: {{ user.name }}, <br />
uuid: {{ user.uuid }}
</div>`,
inject: ['user'],
created() {
console.log(this.user);

// 如果根元件沒有使用 function return 則不能使用響應式
this.user.name = '小美';
}
}

const app = Vue.createApp({
data() {
return {
user: {
name: '小明',
uuid: 78163
}
}
},
provide(){
return {
user: this.user
}
},
})

app.component('card', {
data() {
return {
title: '文件標題',
content: '文件內文',
toggle: false
}
},
components: {
user
},
inject: ['user'],
template: `
<div class="card" style="width: 18rem;">
<div className="card-header">card 元件,姓名{{ user.name }}</div>
<div class="card-body">
<user></user>
</div>
</div>
`,
});

app.mount('#app');
</script>
</body>
</html>

Composition API

非響應式

<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.global.js"></script>
</head>
<body class="bg-lighter">

<div id="app">
<card >
</card>
</div>
<script type="module">
const { createApp, ref, provide, inject } = Vue;

const card = {
template: `<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">{{ person.name }}</h5>
<input type="text" v-model="person.name">
</div>
</div>`,
setup(props) {

const person = inject('person');

return {
person
}
}
}

const app = createApp({
components: {
card,
},
setup(props) {
const person = {
name: 'Joe'
};

provide('person', person);

return {
person,
}
},
});

app.mount('#app')
</script>
</body>
</html>

響應式

<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.global.js"></script>
</head>
<body class="bg-lighter">

<div id="app">
<card >
</card>
</div>
<script type="module">
const { createApp, ref, provide, inject } = Vue;

const card = {
template: `<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">{{ person.name }}</h5>
<input type="text" v-model="person.name">
</div>
</div>`,
setup(props) {

const person = inject('person');

return {
person
}
}
}

const app = createApp({
components: {
card,
},
setup(props) {
const person = ref({
name: 'Joe'
});

provide('person', person);

return {
person,
}
},
});

app.mount('#app')
</script>
</body>
</html>