slogan3

Maak een Vue.js Component met Geavanceerde State Management Integratie zonder Vuex

description

Met deze prompt leer je hoe je geavanceerde state management in Vue.js kunt toepassen zonder afhankelijk te zijn van Vuex. Dit maakt je applicatie lichter en flexibeler, en helpt je om meer controle te krijgen over de state-synchronisatie tussen componenten. Het biedt een alternatief voor complexe state management bibliotheken en bevordert het gebruik van moderne Vue 3 technieken.

prompt

Help me een Vue.js component te maken dat geavanceerde state management toepast zonder gebruik te maken van Vuex. Beschrijf hoe ik gebruik kan maken van de Composition API en provide/inject om de state tussen componenten te delen en te s ...

try_prompt

generate_helper
disclaimerOnPageApi

ai_answers

provider: openai
model: gpt-4.1-nano
Hier is een voorbeeld van hoe je een Vue.js component kunt maken die geavanceerd state management toepast zonder Vuex, door gebruik te maken van de Composition API en provide/inject. Dit voorbeeld toont een winkelwagen met een productlijst, aantal producten en totaalprijs, en zorgt dat updates direct gesynchroniseerd worden. **Stap 1: Creëer een centrale state met composable** Maak een aparte JavaScript-bestand (bijvoorbeeld `useWinkelwagen.js`) waarin je de state definieert en functies om deze te muteren. ```js // useWinkelwagen.js import { reactive } from 'vue'; export function useWinkelwagen() { const state = reactive({ producten: [], // [{ id, naam, prijs, aantal }] totaalPrijs: 0, totaalAantal: 0 }); function voegProductToe(product) { const bestaand = state.producten.find(p => p.id === product.id); if (bestaand) { bestaand.aantal++; } else { state.producten.push({ ...product, aantal: 1 }); } updateTotaal(); } function wijzigAantal(productId, nieuwAantal) { const product = state.producten.find(p => p.id === productId); if (product) { product.aantal = nieuwAantal; if (product.aantal <= 0) { // verwijder product als aantal 0 state.producten = state.producten.filter(p => p.id !== productId); } updateTotaal(); } } function updateTotaal() { let totaalPrijs = 0; let totaalAantal = 0; for (const p of state.producten) { totaalPrijs += p.prijs * p.aantal; totaalAantal += p.aantal; } state.totaalPrijs = totaalPrijs; state.totaalAantal = totaalAantal; } return { state, voegProductToe, wijzigAantal }; } ``` **Stap 2: Provider component** Maak een hoofdcomponent die de winkelwagen state voorziet voor andere componenten. ```vue <!-- WinkelwagenProvider.vue --> <template> <slot></slot> </template> <script> import { provide } from 'vue'; import { useWinkelwagen } from './useWinkelwagen'; export default { setup() { const winkelwagen = useWinkelwagen(); provide('winkelwagen', winkelwagen); } } </script> ``` **Stap 3: Consumer componenten** Andere componenten kunnen de state injecteren en gebruiken. ```vue <!-- ProductLijst.vue --> <template> <div> <div v-for="product in beschikbareProducten" :key="product.id"> <span>{{ product.naam }} - €{{ product.prijs }}</span> <button @click="voegToe(product)">Voeg toe</button> </div> </div> </template> <script> import { inject } from 'vue'; export default { setup() { const { voegProductToe } = inject('winkelwagen'); const beschikbareProducten = [ { id: 1, naam: 'Product A', prijs: 10 }, { id: 2, naam: 'Product B', prijs: 20 }, ]; function voegToe(product) { voegProductToe(product); } return { beschikbareProducten, voegToe }; } } </script> ``` ```vue <!-- WinkelwagenDisplay.vue --> <template> <div> <h2>Winkelwagen</h2> <ul> <li v-for="product in winkelwagen.state.producten" :key="product.id"> {{ product.naam }} - €{{ product.prijs }} x {{ product.aantal }} <button @click="verhoogAantal(product)">+</button> <button @click="verlaagAantal(product)">-</button> </li> </ul> <p>Totaalproducten: {{ winkelwagen.state.totaalAantal }}</p> <p>Totaalprijs: €{{ winkelwagen.state.totaalPrijs }}</p> </div> </template> <script> import { inject } from 'vue'; export default { setup() { const winkelwagen = inject('winkelwagen'); function verhoogAantal(product) { winkelwagen.wijzigAantal(product.id, product.aantal + 1); } function verlaagAantal(product) { winkelwagen.wijzigAantal(product.id, product.aantal - 1); } return { winkelwagen, verhoogAantal, verlaagAantal }; } } </script> ``` **Samenvatting:** - Je creëert een centrale state via een composable (`useWinkelwagen`) die je via `provide` beschikbaar stelt. - Andere componenten injecteren deze state met `inject`. - Omdat de state reactiviteit gebruikt (`reactive`), worden alle wijzigingen automatisch gesynchroniseerd tussen componenten. - De functies muteren de state en zorgen dat de totaalprijs en aantal direct worden bijgewerkt. Zo heb je een geavanceerde, reactieve state management implementatie zonder Vuex, volledig gebruik makend van Vue's Composition API en provide/inject.
provider: deepseek
model: deepseek-chat
Hier is een volledige implementatie van een Vue.js winkelwagen met geavanceerd state management: ## 1. State Management Service (composables/useCart.js) ```javascript import { ref, computed, readonly } from 'vue' // Centrale state buiten component (singleton pattern) const cartItems = ref([]) export function useCart() { // Computed totaalprijs const totalPrice = computed(() => { return cartItems.value.reduce((total, item) => { return total + (item.price * item.quantity) }, 0) }) // Computed totaal aantal producten const totalItems = computed(() => { return cartItems.value.reduce((total, item) => { return total + item.quantity }, 0) }) // Acties/mutations const addToCart = (product) => { const existingItem = cartItems.value.find(item => item.id === product.id) if (existingItem) { existingItem.quantity += 1 } else { cartItems.value.push({ ...product, quantity: 1 }) } } const removeFromCart = (productId) => { const index = cartItems.value.findIndex(item => item.id === productId) if (index !== -1) { cartItems.value.splice(index, 1) } } const updateQuantity = (productId, newQuantity) => { const item = cartItems.value.find(item => item.id === productId) if (item) { if (newQuantity <= 0) { removeFromCart(productId) } else { item.quantity = newQuantity } } } const clearCart = () => { cartItems.value = [] } // Readonly state voor veilige toegang return { items: readonly(cartItems), totalPrice, totalItems, addToCart, removeFromCart, updateQuantity, clearCart } } ``` ## 2. Provider Component (CartProvider.vue) ```vue <template> <slot /> </template> <script setup> import { provide } from 'vue' import { useCart } from '../composables/useCart' const cart = useCart() // Provide de volledige cart functionaliteit provide('cart', { items: cart.items, totalPrice: cart.totalPrice, totalItems: cart.totalItems, addToCart: cart.addToCart, removeFromCart: cart.removeFromCart, updateQuantity: cart.updateQuantity, clearCart: cart.clearCart }) </script> ``` ## 3. Hoofd Winkelwagen Component (ShoppingCart.vue) ```vue <template> <div class="shopping-cart"> <h2>Winkelwagen ({{ totalItems }} items)</h2> <div class="cart-items"> <CartItem v-for="item in items" :key="item.id" :item="item" /> </div> <div class="cart-summary"> <p>Totaalprijs: €{{ totalPrice.toFixed(2) }}</p> <button @click="clearCart" :disabled="totalItems === 0"> Winkelwagen legen </button> </div> </div> </template> <script setup> import { inject } from 'vue' import CartItem from './CartItem.vue' const cart = inject('cart') // Directe toegang tot de geïnjecteerde state const { items, totalPrice, totalItems, clearCart } = cart </script> <style scoped> .shopping-cart { border: 1px solid #ccc; padding: 20px; border-radius: 8px; } .cart-summary { margin-top: 20px; padding-top: 20px; border-top: 1px solid #eee; } </style> ``` ## 4. Cart Item Component (CartItem.vue) ```vue <template> <div class="cart-item"> <div class="item-info"> <h3>{{ item.name }}</h3> <p>€{{ item.price.toFixed(2) }} per stuk</p> </div> <div class="quantity-controls"> <button @click="decreaseQuantity">-</button> <span class="quantity">{{ item.quantity }}</span> <button @click="increaseQuantity">+</button> </div> <div class="item-total"> €{{ (item.price * item.quantity).toFixed(2) }} </div> <button @click="removeItem" class="remove-btn">🗑️</button> </div> </template> <script setup> import { inject } from 'vue' const props = defineProps({ item: { type: Object, required: true } }) const cart = inject('cart') const increaseQuantity = () => { cart.updateQuantity(props.item.id, props.item.quantity + 1) } const decreaseQuantity = () => { cart.updateQuantity(props.item.id, props.item.quantity - 1) } const removeItem = () => { cart.removeFromCart(props.item.id) } </script> <style scoped> .cart-item { display: flex; align-items: center; justify-content: space-between; padding: 10px; border-bottom: 1px solid #eee; } .quantity-controls { display: flex; align-items: center; gap: 10px; } .quantity-controls button { width: 30px; height: 30px; border: 1px solid #ccc; background: white; cursor: pointer; } .remove-btn { background: #ff4444; color: white; border: none; padding: 5px 10px; cursor: pointer; border-radius: 4px; } </style> ``` ## 5. Product Component (ProductItem.vue) ```vue <template> <div class="product-item"> <h3>{{ product.name }}</h3> <p>€{{ product.price.toFixed(2) }}</p> <button @click="addToCart" :disabled="isInCart"> {{ isInCart ? 'In winkelwagen' : 'Toevoegen' }} </button> </div> </template> <script setup> import { inject, computed } from 'vue' const props = defineProps({ product: { type: Object, required: true } }) const cart = inject('cart') const isInCart = computed(() => { return cart.items.value.some(item => item.id === props.product.id) }) const addToCart = () => { cart.addToCart(props.product) } </script> <style scoped> .product-item { border: 1px solid #ddd; padding: 15px; margin: 10px; border-radius: 5px; } </style> ``` ## 6. Hoofd App Component (App.vue) ```vue <template> <CartProvider> <div class="app"> <header> <h1>Mijn Winkel</h1> <CartSummary /> </header> <main> <ProductList /> <ShoppingCart /> </main> </div> </CartProvider> </template> <script setup> import CartProvider from './components/CartProvider.vue' import CartSummary from './components/CartSummary.vue' import ProductList from './components/ProductList.vue' import ShoppingCart from './components/ShoppingCart.vue' </script> ``` ## 7. Cart Summary Component (CartSummary.vue) ```vue <template> <div class="cart-summary-header"> <span>🛒 {{ totalItems }} items - €{{ totalPrice.toFixed(2) }}</span> </div> </template> <script setup> import { inject } from 'vue' const cart = inject('cart') const { totalItems, totalPrice } = cart </script> ``` ## Belangrijke kenmerken van deze architectuur: 1. **Singleton State**: De state leeft buiten de componenten en wordt gedeeld 2. **Readonly State**: Externe componenten kunnen de state niet direct muteren 3. **Centralized Actions**: Alle mutations verlopen via gedefinieerde functies 4. **Reactive Updates**: Wijzigingen worden automatisch doorgevoerd in alle componenten 5. **Type Safety**: Duidelijke scheiding tussen state en acties Deze aanpak biedt vergelijkbare voordelen als Vuex maar met minder boilerplate en meer flexibiliteit dankzij de Composition API.