Vuex + Mock购物车数据模拟实例

789 阅读6分钟

后端数据模拟

根目录创建 vue.config.js,写入接口和数据,安装 cnpm i axios -S ,重启服务器

module.exports = {
    devServer: {
        before(app, serve) {
            app.get('/api/products', (req, res) => {
                res.json({
                    results: products
                })
            })
        }
    }
}

const products = [
    { id: 1, title: 'iphone11', price: 800, inventory: 10 },
    { id: 2, title: 'iphone11 pro', price: 1200, inventory: 15 },
    { id: 3, title: 'iphone11 pro max', price: 1400, inventory: 7 }
]

获取数据 & 渲染数据列表

vuex 产品模块 products.js 内引入axios 并异步发送请求获取数据

import Axiso from 'axios'

export default {
    namespaced: true,
    state: {
        products: []
    },
    mutations: {
        getAllProducts(state, results) {
            state.products = results; //3.将获得的数据赋值给当前state内的products
        }
    },
    actions: {
        async getAllProducts({ commit }) { //2.发送异步请求获取数据 提交mutation
            try {
                const res = await Axiso.get('/api/products'); //向接口请求数据
                const results = res.data.results; //将获得的数据提取赋值给新的属性
                commit('getAllProducts', results) //commit调用mutation并传递获得的数据
            } catch (error) {
                console.log(error);
            }
        }
    }
}

获取到数据:

data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}

products.vue组件中:

<template>
	<div>
		<h2>商品</h2>
		<ul><!-- 5.渲染产品列表 -->
			<li v-for="product in products" :key="product.id">
				<h3>{{product.title}} - {{product.price}}</h3>
			</li>
		</ul>
	</div>
</template>

<script>
import { mapState } from "vuex";
export default {
	computed: {
		...mapState("products", ["products"]) //4.辅助函数获取store内products模块的状态
	},
	created() {
		this.$store.dispatch("products/getAllProducts");//1.组件创建后调用store内产品组件的Action获取服务器数据
	}
};
</script>

fliter修饰数据

main.js 内注册全局过滤器:

Vue.filter('currency', (value) => {
  return '$' + value;
})

组件内引用:

<ul>
    <li v-for="product in products" :key="product.id">
        <h3>{{product.title}} - {{product.price | currency}}</h3>
    </li>
</ul>

注册全局组件

main.js 内注册全局组件:

import CartList from '@/components/CartList.vue' //引入组件

Vue.component(CartList.name, CartList) //注册全局组件
export default {
	name: "CartList" //组件名 注册全局组件时使用
};

向购物车模块添加数据

产品组件内获取vuex购物车模块内的方法并添加事件按钮触发并传值

<template>
	<div>
		<h2>产品</h2>
		<ul>
			<li v-for="product in products" :key="product.id">
				<h3>{{product.title}} - {{product.price | currency}} - {{product.inventory}}</h3>
                <!-- 2.添加点击事件触发action并传值当前点击的数据对象给store(会包括列表渲染里没有引用的) -->
				<button @click="addProductToCart(product)">添加到购物车</button>
			</li>
		</ul>
	</div>
</template>

<script>
import { mapState, mapActions } from "vuex";
export default {
	computed: {
		...mapState("products", ["products"])
	},
	created() {
		this.$store.dispatch("products/getAllProducts");
	},
	methods: {
        //1.获取vuex购物车模块内的方法
		...mapActions("cartList", ["addProductToCart"])
	}
};
</script>

购物车模块接收触发事件和传入的数据,进行判断并存储和修改购物车数据

export default {
    namespaced: true,
    state: {
        cartList: [] //3.声明购物车状态
    },
    getters: {
        evenOrOdd2(state) {
            return state.count % 2 === 0 ? '偶数' : '奇数'
        }
    },
    mutations: {
        //7.添加到购物车产品名称
        pushProductToCart(state, { id, quantity }) {
            state.cartList.push({
                id,
                quantity
            })
        },
        //7.产品数量加一
        addProductQuantity(state, { id }) {
            //根据传入的数据的id,获取cartList对应id数据
            const product = state.cartList.find(item => item.id === id);
            //对这条数据的数量加一
            product.quantity++;
        }
    },
    actions: {
        addProductToCart({ commit, state }, product) { //4.调用添加购物车方法传入state,接收product
            if (product.inventory > 0) { //5.从传入的product全部数据中的inventory判断库存是否大于0
                //获取购物车cartList中的数据,判断item中对应id的数据,和传入的数据,是否一样并赋值一个变量
                const cartItem = state.cartList.find(item => item.id === product.id);
                console.log(cartItem);

                if (!cartItem) { //6.cartList对比传入数据的id为空,没有这条数据,cartItem为faulse
                    //购物车没有数据 把数据添加到cartList
                    commit('pushProductToCart', { id: product.id, quantity: 1 })
                } else {
                    //购物车中已有数据 传递这个产品的id到产品添加方法
                    commit('addProductQuantity', { id: product.id })
                }
            }

        }
    }
}

渲染购物车列表

vuex购物车模块内getter方法生成数据

state: {
	cartList: [],
},
getters: {
    //通过getter的第三个参数获得产品模块的数据和当前购物车模块的cartList数据比对,生成购物车数据
    getCartList(state, getters, rootState) {
        //通过map将cartList的数据遍历为有序列表并解构出id和quantity,
        //返回的是map产生的新数组,map不会对空数组进行检测
        return state.cartList.map(({ id, quantity }) => {
            /*第一个products是模块名,第二个是状态名,获取产品模块的所有产品数据的一个数组,
            通过find方法内的函数判断,map对cartList的每次遍历对产品全部数据进行逐个比对,
            获得相同id的对象对product进行赋值*/
            const product = rootState.products.products.find(item => item.id === id);
            return { //cartList的每一产品id对应的product对象,
                //获取产品数据的title和price和cartList.id的数量,返回给map存储到新的数组
                title: product.title, //title为product的title
                price: product.price, //price为product的title
                quantity //数量为当前购物车模块内对应id的数量
            }
        })
    }
}

购物车组件获取getter生成的购物车数据

<template>
	<div>
		<h2>购物车</h2>
		<ul><!-- 将getter数据渲染为列表 -->
			<li v-for="(item,index) in getCartList" :key="index">
				<h3>{{item.title}} - {{item.price | currency}} × {{item.quantity}}</h3>
			</li>
		</ul>
	</div>
</template>

<script>
import { mapGetters } from "vuex";
export default {
	name: "CartList",
	computed: {
        //获取vuex购物车模块的getter方法生成的购物车数据
		...mapGetters("cartList", ["getCartList"])
	}
};
</script>

总价格计算

vuex购物车模块内:

getters: {
    getCartList(state, getters, rootState) {
        return state.cartList.map(({ id, quantity }) => {
            const product = rootState.products.products.find(item => item.id === id);
            return {
                title: product.title,
                price: product.price,
                quantity
            }
        })
    },
    //1.添加购物车合计价格方法,并传入第二个参数getters来获取另一个getter
    cartTotalPrice(state, getters) { 
        //2.获得上方getCartList返回的数组用reduce()方法计算并传入getCartList生成的数组product
        return getters.getCartList.reduce((total, product) => {
            //3.total作为累加器并从0开始累加product的价格和数量相乘的单个产品的总价,reduce()方法返回相加所有产品的总价
            return total + product.price * product.quantity
        }, 0);
    }
},

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终相加计算为一个值,因为购物车在没用数据的时候是空值,reduce()方法在传入对象为空时会报错,传入默认值,并从0开始计算

<template>
	<div>
		<h2>购物车</h2>
		<ul>
			<li v-for="(item,index) in getCartList" :key="index">
				<h3>{{item.title}} - {{item.price | currency}} × {{item.quantity}}</h3>
			</li>
		</ul>
        <!-- 5.引用方法并添加全局过滤器 -->
		<h3>总价格: {{cartTotalPrice | currency}}</h3>
	</div>
</template>

<script>
import { mapGetters } from "vuex";
export default {
	name: "CartList",
	computed: {
        //4.辅助函数内添加vuex购物车模块总价计算的方法
		...mapGetters("cartList", ["getCartList", "cartTotalPrice"])
	}
};
</script>

减库存

在购物车模块内的添加产品到购物车方法下,提交对产品模块内方法的调用

actions: {
    addProductToCart({ commit, state }, product) {
        if (product.inventory > 0) {
            const cartItem = state.cartList.find(item => item.id === product.id);
            if (!cartItem) {
                commit('pushProductToCart', { id: product.id, quantity: 1 })
            } else {
                commit('addProductQuantity', { id: product.id })
            }
            //每次点击加入购物车,触发products模块内的这个方法,并传值当前点击product的id
            //如果想在一个模块中提交另一个模块中的方法,那么需要第三个参数 { root:true } 来返回到根
            commit('products/decrementProductsInventory', { id: product.id }, { root: true })
        }
    }
}

产品模块的mutations内:

mutations: {
    //方法传入产品数据,并解构出传入产品的id
    decrementProductsInventory(state, { id }) {
        //模块内的数据比对传入的id,获得对应的产品数据
        const product = state.products.find(item => item.id === id);
        //每次触发产品的库存减一
        product.inventory--;
    }
}

库存为0时按钮不可点击

通过绑定button的 disabled 事件判断当前产品的库存是否为0,为0时为faulse,取反为true,让 disabled 生效

<button :disabled="!product.inventory" @click="addProductToCart(product)">添加到购物车</button>