Vue 3 Advanced Features and Pinia State Management
Advanced Implementation Details
Component Name Configuration
Setting component name properties enhances debugging and tooling support:
<script setup>
// Supported in Vue 3.3+
defineOptions({
name: 'ComponentName',
inheritAttrs: false
})
</script>
Slot Systems and Scoped Slots
Understanding different slot types provides flexible component composition:
<template>
<component-wrapper>
<div>Default slot content</div>
<template #named-slot>
<div>Named slot implementation</div>
</template>
<template #scoped-slot="slotData">
<div>Scoped slot with data: {{ slotData }}</div>
</template>
</component-wrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ComponentWrapper from '@/components/ComponentWrapper.vue'
export default defineComponent({
name: 'ParentComponent',
components: {
ComponentWrapper
}
})
</script>
The rendering implementation handles different slot types:
<script lang="ts">
import { defineComponent, CreateElement, VNode } from 'vue'
export default defineComponent({
name: 'ComponentWrapper',
render(createElement: CreateElement): VNode {
console.log(this.$slots) // Excludes scoped slots
console.log(this.$scopedSlots) // Supports value passing
return createElement('div', {}, [
this.$slots.default,
this.$slots['named-slot'],
this.$scopedSlots!.default!(null),
this.$scopedSlots!['named-slot']!(null),
this.$scopedSlots!['scoped-slot']!('scoped-data')
])
}
})
</script>
Template Iteration Patterns
Different iteration methods serve distinct purposes in Vue templates:
<template>
<div>
<div v-for="(item, index) of itemsArray" :key="index">{{ item }}</div>
<div v-for="(value, key) in dataObject" :key="key">{{ key }}: {{ value }}</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'IterationExample',
data() {
return {
itemsArray: ['Mammoth', 'Axe Warrior', 'Mars'],
dataObject: {
name: 'Ice Maiden',
kills: 10,
deaths: 0
}
}
}
})
</script>
TypeScript Functional Components
Functional components provide lightweight alternatives for simple UI elements:
Named functional component approach:
import type { SetupContext } from 'vue'
type MessageComponentProps = {
textContent: string
}
type EventHandlers = {
sendText(content: string): void
}
function MessageComponent(
props: MessageComponentProps,
context: SetupContext<eventhandlers>
) {
return (
<button onClick={() => context.emit('sendText', props.textContent)}>
{props.textContent} {' '}
</button>
)
}
MessageComponent.props = {
textContent: {
type: String,
required: true
}
}
MessageComponent.emits = {
sendText: (value: unknown) => typeof value === 'string'
}
MessageComponent.inheritAttrs = false
MessageComponent.displayName = 'MessageComponent'
</eventhandlers>
Anonymous funcitonal component pattern:
import type { FunctionalComponent } from 'vue'
const MessageComponent: FunctionalComponent<messagecomponentprops eventhandlers=""> = (
props,
context
) => {
return (
<button onClick={() => context.emit('sendText', props.textContent)}>
{props.textContent} {' '}
</button>
)
}
</messagecomponentprops>
Pinia State Management
Application Initialization
Main application setup integrates Pinia for global state management:
import App from './App.vue'
import router from './router'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
Store Structure Organization
Store definitions encapsulate state, getters, and actions in organized modules:
import { defineStore } from 'pinia';
interface ProductItem {
id: number;
title: string;
quantity: number;
}
interface ShoppingCartState {
customerName: string;
customerAge: number;
productList: ProductItem[];
}
export const useShoppingStore = defineStore("shopping-cart", {
state: (): ShoppingCartState => {
return {
customerName: 'Xiao Meng',
customerAge: 18,
productList: [{id: 1, title: 'Mobile Phone', quantity: 1}]
}
},
getters: {
userCategory: (state) => {
console.log('getter context:', this)
return state.customerAge < 18 ? 'Minor' : 'Adult';
},
getProductById (state) {
console.log('getter access:', state.customerName, this.customerName);
return (id: number) => this.productList.find(item => item.id == id);
}
},
actions: {
addProduct (product: ProductItem) {
if (product.quantity) product.quantity = product.quantity / 1;
const existingItem = this.productList.find(item => item.id == product.id);
if (existingItem) {
existingItem.quantity += product.quantity;
} else {
this.productList.push(product);
}
},
}
})
Alternative store definition using setup syntax:
import { defineStore } from "pinia";
import { ref, computed } from 'vue';
export const useNumberStore = defineStore('number-counter', () => {
const currentCount = ref(0);
const userName = ref('Zhang San');
const doubledValue = computed(() => currentCount.value * 2);
function incrementCount() {
currentCount.value++;
}
return {
currentCount,
doubledValue,
incrementCount
}
})
View Integration and Usage
Components interact with stores through various methods and patterns:
<script setup lang="ts">
import { useShoppingStore } from '@/stores/shopping';
import { useNumberStore } from '@/stores/number';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
const shoppingStore = useShoppingStore();
const numberStore = useNumberStore();
const { customerAge } = storeToRefs(shoppingStore);
function updateCustomerAge() {
shoppingStore.$patch((state) => {
state.customerName = 'Xiao Fei';
state.customerAge = 35;
state.productList.push({id: state.productList.length+1, title: 'Computer', quantity: 2})
})
}
function resetCustomerData() {
shoppingStore.$reset();
}
shoppingStore.$subscribe((mutation, state) => {
console.log('State monitoring', mutation.storeId, state.customerName)
localStorage.setItem('shopping-cart', JSON.stringify(state));
})
const productId = ref<number>(1);
const productName = ref<string>('');
const productQuantity = ref<number>(1);
function addToCart() {
const product = {id: productId.value, title: productName.value, quantity: productQuantity.value};
shoppingStore.addProduct(product);
}
</script>
<template>
<div>
<p>Customer: {{ shoppingStore.customerName }}</p>
<p>Age: {{ customerAge }}</p>
<p>Products:</p>
<ul>
<li v-for="(item, index) in shoppingStore.productList" :key="index">
ID: {{ item.id }},Title: {{ item.title }},Quantity: {{ item.quantity }}
</li>
</ul>
<input v-model="productId" placeholder="Product ID">
<input v-model="productName" placeholder="Product Title">
<input v-model="productQuantity" placeholder="Quantity">
<button @click="addToCart">Add to Cart</button>
<br>
<br>
<button @click="updateCustomerAge">Update Age</button>
<br><br>
<button @click="resetCustomerData">Reset Data</button>
<p>Category: {{ shoppingStore.userCategory }}</p>
<p>Product Lookup: {{ shoppingStore.getProductById(1) }}</p>
<h3>Setup Store Example</h3>
<p>Count: {{ numberStore.currentCount }} , {{ numberStore.doubledValue }}</p>
<button @click="numberStore.incrementCount">Increment</button>
</div>
</template>