Laravel 9+Vue 3中如何实现事件触发即时生效?
问题描述
我使用Laravel 9、Vue.js 3和Vite开发一个简单电商网站,点击「加入购物车」按钮后功能正常,但购物车图标上的数量必须刷新页面才会更新。以下是相关代码:
加入购物车组件
<template> <div class="flex items-center justify-between py-4"> <button class=" bg-orange-700 text-black rounded-md cursor-pointer text-xs font-semibold px-3 py-2 m-4 " v-on:click.prevent="addToCart" > Ajouter au panier </button> </div> </template> <script setup > import useProduct from '../composables/products' import axios from 'axios'; import Emitter from 'pico-emitter'; const {add} = useProduct() const productId = defineProps(['productId']) const emitter = new Emitter(); const addToCart= async()=>{ await axios.get('/sanctum/csrf-cookie'); await axios.get('/api/user') .then(async(res)=>{ let cartCount= await add(productId); emitter.emit('refreshCartCount',cartCount); console.log(cartCount) }) .catch(err=>console.log(err)); } </script>
导航栏购物车组件
<template> <div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex mr-10"> <a href="/shoppingCart" class="relative"> <span class="absolute -top-1 left-4 rounded-full bg-indigo-700 w-5 h-5 text-xs text-white flex items-center justify-center">{{ cartCount }}</span> <svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-sm font-medium leading-5 text-gray-600 " fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> <path stroke-linecap="round" stroke-linejoin="round" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" /> </svg> </a> </div> </template> <script setup> import Emitter from 'pico-emitter'; import {onMounted,ref} from 'vue'; import useProduct from '../composables/products'; const {getCount}= useProduct(); const cartCount=ref(0) const emitter = new Emitter(); emitter.on('refreshCartCount',function(count){ cartCount.value = count }); onMounted(async()=>{ cartCount.value = await getCount(); console.log(cartCount.value) }); </script>
逻辑处理文件(composables/products.js)
import axios from "axios"; export default function useProduct(){ const add = async(productId)=>{ let response = await axios.post('/api/products',{ productId: productId }); return response.data.count; } const getCount = async() =>{ let response = await axios.get('/api/products/count'); return response.data.count; } return { add, getCount } }
问题根源与修复方案
问题核心
你代码中的两个组件各自创建了独立的Emitter实例,二者完全没有关联。加入购物车组件触发的refreshCartCount事件,导航栏组件的监听器根本接收不到,所以数量不会实时更新。
修复方法
方法1:使用全局共享的Emitter实例
创建一个全局Emitter实例,让两个组件共用:
- 新建
emitter.js文件:
import Emitter from 'pico-emitter'; export default new Emitter();
- 修改加入购物车组件的script部分:
// 替换原有Emitter导入和实例化代码 import emitter from '../emitter'; const addToCart= async()=>{ await axios.get('/sanctum/csrf-cookie'); await axios.get('/api/user') .then(async(res)=>{ let cartCount= await add(productId); emitter.emit('refreshCartCount',cartCount); console.log(cartCount) }) .catch(err=>console.log(err)); }
- 修改导航栏组件的script部分:
// 替换原有Emitter导入和实例化代码 import emitter from '../emitter'; emitter.on('refreshCartCount',function(count){ cartCount.value = count });
方法2:使用Vue内置的Provide/Inject(更贴合Vue生态)
利用Vue的依赖注入传递更新逻辑:
- 在根组件(如
App.vue)中提供全局状态和更新方法:
<script setup> import { ref, provide, onMounted } from 'vue'; import useProduct from './composables/products'; const { getCount } = useProduct(); const cartCount = ref(0); provide('cartCount', cartCount); provide('updateCartCount', (newCount) => { cartCount.value = newCount; }); onMounted(async () => { cartCount.value = await getCount(); }); </script>
- 修改加入购物车组件:
import { inject } from 'vue'; const updateCartCount = inject('updateCartCount'); const addToCart= async()=>{ await axios.get('/sanctum/csrf-cookie'); await axios.get('/api/user') .then(async(res)=>{ let cartCount= await add(productId); updateCartCount(cartCount); console.log(cartCount) }) .catch(err=>console.log(err)); }
- 修改导航栏组件:
import { inject } from 'vue'; const cartCount = inject('cartCount');
方法3:使用Pinia状态管理(推荐用于中大型项目)
购物车属于全局状态,用Pinia管理更规范:
- 安装Pinia:
npm install pinia
- 创建
stores/cart.js:
import { defineStore } from 'pinia'; import axios from 'axios'; export const useCartStore = defineStore('cart', { state: () => ({ count: 0 }), actions: { async addProduct(productId) { const response = await axios.post('/api/products', { productId }); this.count = response.data.count; }, async fetchCount() { const response = await axios.get('/api/products/count'); this.count = response.data.count; } } });
- 在
main.js注册Pinia:
import { createPinia } from 'pinia'; app.use(createPinia());
- 修改加入购物车组件:
import { useCartStore } from '../stores/cart'; const cartStore = useCartStore(); const addToCart= async()=>{ await axios.get('/sanctum/csrf-cookie'); await axios.get('/api/user') .then(async()=>{ await cartStore.addProduct(productId); }) .catch(err=>console.log(err)); }
- 修改导航栏组件:
import { useCartStore } from '../stores/cart'; import { onMounted } from 'vue'; const cartStore = useCartStore(); onMounted(async()=>{ await cartStore.fetchCount(); });
模板中直接使用{{ cartStore.count }}即可。
内容的提问来源于stack exchange,提问作者Arthur
相关产品推荐
相关产品推荐

