[Medium] π ΠΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΡ ΠΌΠ΅ΠΆΠ΄Ρ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°ΠΌΠΈ
1. ΠΠ°ΠΊΠΈΠ΅ ΡΡΡΠ΅ΡΡΠ²ΡΡΡ ΡΠΏΠΎΡΠΎΠ±Ρ ΠΊΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΠΈ ΠΌΠ΅ΠΆΠ΄Ρ Vue-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°ΠΌΠΈ?β
ΠΠ°ΠΊΠΈΠ΅ ΠΏΠ°ΡΡΠ΅ΡΠ½Ρ ΠΊΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΠΈ ΡΡΡΠ΅ΡΡΠ²ΡΡΡ ΠΌΠ΅ΠΆΠ΄Ρ Vue-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°ΠΌΠΈ?
Π‘ΡΡΠ°ΡΠ΅Π³ΠΈΡ ΠΊΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΠΈ Π·Π°Π²ΠΈΡΠΈΡ ΠΎΡ ΡΠΈΠΏΠ° ΠΎΡΠ½ΠΎΡΠ΅Π½ΠΈΠΉ ΠΌΠ΅ΠΆΠ΄Ρ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°ΠΌΠΈ.
ΠΠ°ΡΠ΅Π³ ΠΎΡΠΈΠΈ ΠΎΡΠ½ΠΎΡΠ΅Π½ΠΈΠΉβ
Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ <-> ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ: props / emit / v-model / refs
ΠΡΠ΅Π΄ΠΎΠΊ <-> ΠΠΎΡΠΎΠΌΠΎΠΊ: provide / inject
Π‘ΠΎΡΠ΅Π΄Π½ΠΈΠ΅ / Π½Π΅ΡΠ²ΡΠ·Π°Π½Π½ΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ: Pinia/Vuex (ΠΈΠ»ΠΈ event emitter Π΄Π»Ρ ΠΏΡΠΎΡΡΡΡ
ΡΠ»ΡΡΠ°Π΅Π²)
1. Props (ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ β Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ)β
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ Π΄Π°Π½Π½ΡΠ΅ Π΄ΠΎΡΠ΅ΡΠ½Π΅ΠΌΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ.
<!-- ParentComponent.vue -->
<template>
<div>
<h1>Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ</h1>
<ChildComponent
:message="parentMessage"
:user="userInfo"
:count="counter"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const parentMessage = ref('ΠΡΠΈΠ²Π΅Ρ ΠΎΡ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ');
const userInfo = ref({ name: 'John', age: 30 });
const counter = ref(0);
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<h2>ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ</h2>
<p>Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅: {{ message }}</p>
<p>ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ: {{ user.name }} ({{ user.age }})</p>
<p>Π‘ΡΡΡΡΠΈΠΊ: {{ count }}</p>
</div>
</template>
<script setup>
defineProps({
message: {
type: String,
required: true,
default: '',
},
user: {
type: Object,
required: true,
default: () => ({}),
},
count: {
type: Number,
default: 0,
validator: (value) => value >= 0,
},
});
</script>
ΠΠ°ΠΌΠ΅ΡΠ°Π½ΠΈΡ ΠΎ propsβ
- Props ΡΠ²Π»ΡΡΡΡΡ ΠΎΠ΄Π½ΠΎΠ½Π°ΠΏΡΠ°Π²Π»Π΅Π½Π½ΡΠΌΠΈ (ΠΈΡΡΠΎΡΠ½ΠΈΠΊ ΠΈΡΡΠΈΠ½Ρ β ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ)
- ΠΠ΅ ΠΈΠ·ΠΌΠ΅Π½ΡΠΉΡΠ΅ props Π½Π°ΠΏΡΡΠΌΡΡ Π² Π΄ΠΎΡΠ΅ΡΠ½Π΅ΠΌ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ΅
- ΠΡΠ»ΠΈ Π½ΡΠΆΠ½ΠΎ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎΠ΅ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅, ΡΠΊΠΎΠΏΠΈΡΡΠΉΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ Π² Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠΉ
ref
<script setup>
import { ref } from 'vue';
const props = defineProps({
message: String,
});
const localMessage = ref(props.message);
</script>
2. Emit (Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ β ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ)β
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ ΡΠ²Π΅Π΄ΠΎΠΌΠ»ΡΠ΅Ρ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ ΡΠ΅ΡΠ΅Π· ΡΠΎΠ±ΡΡΠΈΡ.
<!-- ChildComponent.vue -->
<template>
<div>
<button @click="sendToParent">ΠΡΠΏΡΠ°Π²ΠΈΡΡ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ</button>
<input v-model="inputValue" @input="handleInput" />
</div>
</template>
<script setup>
import { ref } from 'vue';
const emit = defineEmits(['custom-event', 'update:modelValue']);
const inputValue = ref('');
const sendToParent = () => {
emit('custom-event', {
message: 'ΠΡΠΈΠ²Π΅Ρ ΠΎΡ Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°',
timestamp: Date.now(),
});
};
const handleInput = () => {
emit('update:modelValue', inputValue.value);
};
</script>
<!-- ParentComponent.vue -->
<template>
<div>
<h1>Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ</h1>
<ChildComponent
@custom-event="handleCustomEvent"
@update:modelValue="handleUpdate"
/>
<p>ΠΠΎΠ»ΡΡΠ΅Π½ΠΎ: {{ receivedData }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const receivedData = ref(null);
const handleCustomEvent = (data) => {
receivedData.value = data;
};
const handleUpdate = (value) => {
console.log('ΠΠ²ΠΎΠ΄ ΠΎΠ±Π½ΠΎΠ²Π»ΡΠ½:', value);
};
</script>
ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ emits Π² Vue 3β
<script setup>
const emit = defineEmits({
'custom-event': null,
'update:modelValue': (value) => {
if (typeof value !== 'string') {
console.warn('modelValue Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±ΡΡΡ ΡΡΡΠΎΠΊΠΎΠΉ');
return false;
}
return true;
},
});
emit('custom-event', 'data');
</script>
3. v-model (Π΄ Π²ΡΡΡΠΎΡΠΎΠ½Π½ΠΈΠΉ ΠΊΠΎΠ½ΡΡΠ°ΠΊΡ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ-Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ)β
Π‘ΡΠΈΠ»Ρ Vue 2β
<!-- Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ -->
<custom-input v-model="message" />
<!-- ΡΠΊΠ²ΠΈΠ²Π°Π»Π΅Π½Ρ -->
<custom-input :value="message" @input="message = $event" />
<!-- ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ Π² Vue 2 -->
<template>
<input :value="value" @input="$emit('input', $event.target.value)" />
</template>
<script>
export default {
props: ['value'],
};
</script>
Π‘ΡΠΈΠ»Ρ Vue 3β
<!-- Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ -->
<custom-input v-model="message" />
<!-- ΡΠΊΠ²ΠΈΠ²Π°Π»Π΅Π½Ρ -->
<custom-input :modelValue="message" @update:modelValue="message = $event" />
<!-- ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ Π² Vue 3 -->
<template>
<input :value="modelValue" @input="updateValue" />
</template>
<script setup>
defineProps({ modelValue: String });
const emit = defineEmits(['update:modelValue']);
const updateValue = (event) => {
emit('update:modelValue', event.target.value);
};
</script>
ΠΠ½ΠΎΠΆΠ΅ΡΡΠ²Π΅Π½Π½ΡΠ΅ v-model Π² Vue 3β
<!-- Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ -->
<user-form v-model:name="userName" v-model:email="userEmail" />
<!-- ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ -->
<template>
<div>
<input
:value="name"
@input="$emit('update:name', $event.target.value)"
placeholder="ΠΠΌΡ"
/>
<input
:value="email"
@input="$emit('update:email', $event.target.value)"
placeholder="Email"
/>
</div>
</template>
<script setup>
defineProps({
name: String,
email: String,
});
defineEmits(['update:name', 'update:email']);
</script>
4. Provide / Inject (ΠΏΡΠ΅Π΄ΠΎΠΊ β ΠΏΠΎΡΠΎΠΌΠΎΠΊ)β
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: ΠΌΠ΅ΠΆΡΡΠΎΠ²Π½Π΅Π²Π°Ρ ΠΊΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΡ Π±Π΅Π· ΡΠΊΠ²ΠΎΠ·Π½ΠΎΠΉ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΈ props.
<!-- GrandparentComponent.vue -->
<template>
<div>
<h1>ΠΡΠ°ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ</h1>
<parent-component />
</div>
</template>
<script setup>
import { ref, provide } from 'vue';
const userInfo = ref({ name: 'John', role: 'admin' });
const updateUser = (newInfo) => {
userInfo.value = { ...userInfo.value, ...newInfo };
};
provide('userInfo', userInfo);
provide('updateUser', updateUser);
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<h3>ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ</h3>
<p>ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ: {{ userInfo.name }}</p>
<p>Π ΠΎΠ»Ρ: {{ userInfo.role }}</p>
<button @click="changeUser">ΠΠ±Π½ΠΎΠ²ΠΈΡΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ</button>
</div>
</template>
<script setup>
import { inject } from 'vue';
const userInfo = inject('userInfo');
const updateUser = inject('updateUser');
const changeUser = () => {
updateUser({ name: 'Jane', role: 'user' });
};
</script>
ΠΠ°ΠΌΠ΅ΡΠ°Π½ΠΈΡ ΠΎ Provide/Injectβ
- ΠΡΠ»ΠΈΡΠ½ΠΎ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΠΈΡ Π΄Π»Ρ ΠΎΠ±ΡΠ΅Π³ΠΎ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ° Π³Π»ΡΠ±ΠΎΠΊΠΎΠ³ΠΎ Π΄Π΅ΡΠ΅Π²Π° (ΡΠ΅ΠΌΠ°/i18n/ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ)
- ΠΠ΅Π½Π΅Π΅ ΡΠ²Π½ΡΠΉ, ΡΠ΅ΠΌ props, ΠΏΠΎΡΡΠΎΠΌΡ Π²Π°ΠΆΠ½Ρ ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ
- Π Π°ΡΡΠΌΠΎΡΡΠΈΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ readonly + ΡΠ²Π½ΡΠΉ API Π΄Π»Ρ ΠΌΡΡΠ°ΡΠΈΠΉ
<script setup>
import { ref, readonly, provide } from 'vue';
const state = ref({ count: 0 });
provide('state', readonly(state));
provide('updateState', (newState) => {
state.value = newState;
});
</script>
5. Refs (ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ Π½Π°ΠΏΡΡΠΌΡΡ ΠΎΠ±ΡΠ°ΡΠ°Π΅ΡΡΡ ΠΊ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΡ Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ)β
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: ΠΈΠΌΠΏΠ΅ΡΠ°ΡΠΈΠ²Π½ΡΠΉ Π΄ΠΎΡΡΡΠΏ (Π²ΡΠ·ΠΎΠ² ΠΎΡΠΊΡΡΡΡΡ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°, ΡΡΠ΅Π½ΠΈΠ΅ ΠΎΡΠΊΡΡΡΠΎΠ³ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ).
<!-- ParentComponent.vue -->
<template>
<child-component ref="childRef" />
<button @click="callChild">ΠΡΠ·Π²Π°ΡΡ ΠΌΠ΅ΡΠΎΠ΄ Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ</button>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const childRef = ref(null);
const callChild = () => {
childRef.value.someMethod();
};
</script>
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ Ρ ΠΎΡΡΠΎΡΠΎΠΆΠ½ΠΎΡΡΡΡ. Π‘Π½Π°ΡΠ°Π»Π° ΠΏΡΠ΅Π΄ΠΏΠΎΡΠΈΡΠ°ΠΉΡΠ΅ Π΄Π΅ΠΊΠ»Π°ΡΠ°ΡΠΈΠ²Π½ΡΠΉ ΠΏΠΎΡΠΎΠΊ Π΄Π°Π½Π½ΡΡ .
6. $parent / $root (Π½Π΅ ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΡΡΡ)β
ΠΡΡΠΌΠΎΠΉ Π΄ΠΎΡΡΡΠΏ ΠΊ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ/ΠΊΠΎΡΠ½Ρ ΡΠ²Π΅Π»ΠΈΡΠΈΠ²Π°Π΅Ρ ΡΠ²ΡΠ·Π°Π½Π½ΠΎΡΡΡ ΠΈ ΡΡΠ»ΠΎΠΆΠ½ΡΠ΅Ρ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΡΠΎΠΊΠ° Π΄Π°Π½Π½ΡΡ . ΠΡΠ΅Π΄ΠΏΠΎΡΠΈΡΠ°ΠΉΡΠ΅ props/emit/provide ΠΈΠ»ΠΈ Ρ ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅.
7. Event Bus (ΡΡΡΠ°ΡΠ΅Π²ΡΠΈΠΉ/ΠΏΡΠΎΡΡΠΎΠΉ pub-sub)β
Π Vue 2 ΡΠ°ΡΡΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π»ΡΡ new Vue() ΠΊΠ°ΠΊ ΡΠΈΠ½Π° ΡΠΎΠ±ΡΡΠΈΠΉ.
Π Vue 3 ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ Π½Π΅Π±ΠΎΠ»ΡΡΠΎΠΉ ΡΠΌΠΈΡΡΠ΅Ρ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ mitt, ΡΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ Π»ΡΠ³ΠΊΠΈΡ
ΠΊΠ°Π½Π°Π»ΠΎΠ² ΡΠΎΠ±ΡΡΠΈΠΉ.
// eventBus.js
import mitt from 'mitt';
export const emitter = mitt();
<!-- ComponentA.vue -->
<script setup>
import { emitter } from './eventBus';
const sendMessage = () => {
emitter.emit('message-sent', { text: 'ΠΡΠΈΠ²Π΅Ρ', from: 'ComponentA' });
};
</script>
<!-- ComponentB.vue -->
<script setup>
import { onMounted, onUnmounted } from 'vue';
import { emitter } from './eventBus';
const handleMessage = (data) => {
console.log('ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΎ:', data);
};
onMounted(() => emitter.on('message-sent', handleMessage));
onUnmounted(() => emitter.off('message-sent', handleMessage));
</script>
8. Vuex / Pinia (Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΠΎΠ΅ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ΠΌ)β
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: ΠΎΠ±ΡΠ΅Π΅ Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ Π΄Π»Ρ ΡΡΠ΅Π΄Π½ΠΈΡ /ΠΊΡΡΠΏΠ½ΡΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ.
Pinia β ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌΠΎΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π΄Π»Ρ Ρ ΡΠ°Π½ΠΈΠ»ΠΈΡΠ° Π² Vue 3.
// stores/user.js
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
name: '',
email: '',
isLoggedIn: false,
}),
getters: {
fullInfo: (state) => `${state.name} (${state.email})`,
},
actions: {
login(name, email) {
this.name = name;
this.email = email;
this.isLoggedIn = true;
},
logout() {
this.name = '';
this.email = '';
this.isLoggedIn = false;
},
},
});
9. Slots (ΠΏΡΠΎΠ΅ΠΊΡΠΈΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ)β
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ ΡΠ°Π±Π»ΠΎΠ½Π½ΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ Π² ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΡΠ΅ ΠΎΠ±Π»Π°ΡΡΠΈ Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°.
ΠΠ°Π·ΠΎΠ²ΡΠ΅ ΡΠ»ΠΎΡΡβ
<!-- ChildComponent.vue -->
<template>
<div class="card">
<header>
<slot name="header">ΠΠ°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ</slot>
</header>
<main>
<slot>Π‘ΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ</slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- ParentComponent.vue -->
<template>
<child-component>
<template #header>
<h1>ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΉ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ</h1>
</template>
<p>ΠΡΠ½ΠΎΠ²Π½ΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅</p>
<template #footer>
<button>ΠΠΎΠ΄ΡΠ²Π΅ΡΠ΄ΠΈΡΡ</button>
</template>
</child-component>
</template>
Scoped slots (ΡΠ»ΠΎΡΡ Ρ ΠΎΠ±Π»Π°ΡΡΡΡ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΠΈ)β
<!-- ListComponent.vue -->
<template>
<ul>
<li v-for="(item, index) in items" :key="item.id">
<slot :item="item" :index="index"></slot>
</li>
</ul>
</template>
<script setup>
defineProps({ items: Array });
</script>
<!-- ParentComponent.vue -->
<template>
<list-component :items="users">
<template #default="{ item, index }">
<span>{{ index + 1 }}. {{ item.name }}</span>
</template>
</list-component>
</template>
Π ΡΠΊΠΎΠ²ΠΎΠ΄ΡΡΠ²ΠΎ ΠΏΠΎ Π²ΡΠ±ΠΎΡΡ ΡΠΏΠΎΡΠΎΠ±Π° ΠΊΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΠΈβ
| ΠΡΠ½ΠΎΡΠ΅Π½ΠΈΠ΅ | Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌΡΠΉ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ | Π’ΠΈΠΏΠΈΡΠ½ΠΎΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ |
|---|---|---|
| Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ β ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ | Props | ΠΡ ΠΎΠ΄Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ |
| ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ β Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ | Emit | ΠΠ±ΡΠ°ΡΠ½ΡΠΉ Π²ΡΠ·ΠΎΠ² ΡΠΎΠ±ΡΡΠΈΡ |
| Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ β ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ | v-model | Π‘ΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ ΡΠΎΡΠΌΡ |
| ΠΡΠ΅Π΄ΠΎΠΊ β ΠΠΎΡΠΎΠΌΠΎΠΊ | Provide/Inject | ΠΠ»ΡΠ±ΠΎΠΊΠΈΠΉ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ Π΄Π΅ΡΠ΅Π²Π° |
| Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ β ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ (ΠΈΠΌΠΏΠ΅ΡΠ°ΡΠΈΠ²Π½ΠΎ) | Refs | Π Π΅Π΄ΠΊΠΈΠΉ ΠΏΡΡΠΌΠΎΠΉ Π²ΡΠ·ΠΎΠ² ΠΌΠ΅ΡΠΎΠ΄Π° |
| ΠΡΠ±ΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ | Pinia/Vuex | ΠΠ±ΡΠ΅Π΅ Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ |
| ΠΡΠ±ΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ (ΠΏΡΠΎΡΡΠΎ) | Event emitter | ΠΡΠ³ΠΊΠΈΠΉ pub-sub |
| Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ β ΠΠΎΡΠ΅ΡΠ½ΠΈΠΉ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ | Slots | ΠΠΎΠΌΠΏΠΎΠ·ΠΈΡΠΈΡ ΡΠ°Π±Π»ΠΎΠ½ΠΎΠ² |
ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΏΡΠΈΠΌΠ΅Ρ: ΡΡΠ½ΠΊΡΠΈΡ ΠΊΠΎΡΠ·ΠΈΠ½Ρ Ρ Piniaβ
// stores/cart.js
import { defineStore } from 'pinia';
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
}),
getters: {
totalPrice: (state) =>
state.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
itemCount: (state) => state.items.length,
},
actions: {
addItem(product) {
const existing = this.items.find((item) => item.id === product.id);
if (existing) {
existing.quantity++;
} else {
this.items.push({ ...product, quantity: 1 });
}
},
removeItem(productId) {
const index = this.items.findIndex((item) => item.id === productId);
if (index > -1) this.items.splice(index, 1);
},
},
});
2. Π ΡΡΠΌ ΡΠ°Π·Π½ΠΈΡΠ° ΠΌΠ΅ΠΆΠ΄Ρ Props ΠΈ Provide/Inject?β
Π ΡΡΠΌ ΡΠ°Π·Π½ΠΈΡΠ° ΠΌΠ΅ΠΆΠ΄Ρ Props ΠΈ Provide/Inject?
Propsβ
Π₯Π°ΡΠ°ΠΊΡΠ΅ΡΠΈΡΡΠΈΠΊΠΈ:
- Π―Π²Π½ΡΠΉ ΠΈ ΡΡΡΠΊΠΈΠΉ ΠΏΠΎΡΠΎΠΊ Π΄Π°Π½Π½ΡΡ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ-Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ
- ΠΠΎΠ»Π΅Π΅ ΡΡΡΠΎΠ³ΠΎΠ΅ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΡΠΈΠΏΠΎΠ²/ΠΊΠΎΠ½ΡΡΠ°ΠΊΡΠΎΠ²
- ΠΡΠ»ΠΈΡΠ½ΠΎ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΠΈΡ Π΄Π»Ρ ΠΏΡΡΠΌΠΎΠΉ ΠΊΠΎΠΌΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΠΈ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ-Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ
- ΠΠΎΠΆΠ΅Ρ Π²ΡΠ·ΡΠ²Π°ΡΡ ΡΠΊΠ²ΠΎΠ·Π½ΡΡ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ props ΡΠ΅ΡΠ΅Π· ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎ ΡΡΠΎΠ²Π½Π΅ΠΉ
<!-- ΡΠΊΠ²ΠΎΠ·Π½Π°Ρ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠ° ΡΠ΅ΡΠ΅Π· ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ -->
<grandparent>
<parent :data="grandparentData">
<child :data="parentData">
<grandchild :data="childData" />
</child>
</parent>
</grandparent>
Provide/Injectβ
Π₯Π°ΡΠ°ΠΊΡΠ΅ΡΠΈΡΡΠΈΠΊΠΈ:
- ΠΡΠ»ΠΈΡΠ½ΠΎ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΠΈΡ Π΄Π»Ρ ΠΌΠ΅ΠΆΡΡΠΎΠ²Π½Π΅Π²ΡΡ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ
- ΠΠ΅ Π½ΡΠΆΠ½ΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°Π²Π°ΡΡ ΡΠ΅ΡΠ΅Π· ΠΊΠ°ΠΆΠ΄ΡΠΉ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΡΠΉ ΡΠ»ΠΎΠΉ
- ΠΡΠΈ ΡΡΠ΅Π·ΠΌΠ΅ΡΠ½ΠΎΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ ΠΌΠ΅Π½Π΅Π΅ ΠΎΡΠ΅Π²ΠΈΠ΄Π΅Π½ ΠΈΡΡΠΎΡΠ½ΠΈΠΊ Π΄Π°Π½Π½ΡΡ
<grandparent> <!-- provide -->
<parent>
<child>
<grandchild /> <!-- inject -->
</child>
</parent>
</grandparent>
Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°ΡΠΈΡβ
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ Props, ΠΊΠΎΠ³Π΄Π° Π²Π°ΠΆΠ½Π΅Π΅ Π²ΡΠ΅Π³ΠΎ ΡΡΠ½ΠΎΡΡΡ ΠΏΠΎΡΠΎΠΊΠ° Π΄Π°Π½Π½ΡΡ (ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎ ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ-Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ)
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ Provide/Inject Π΄Π»Ρ Π³Π»ΡΠ±ΠΎΠΊΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅Π³ΠΎ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ° (ΡΠ΅ΠΌΠ°, i18n, Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΡ/ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ)
- ΠΠ»Ρ ΠΈΠ·ΠΌΠ΅Π½ΡΠ΅ΠΌΠΎΠ³ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ Π²ΡΠ΅Π³ΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΏΡΠ΅Π΄ΠΏΠΎΡΠΈΡΠ°ΠΉΡΠ΅ Pinia/Vuex