先看效果图:如下
仓库地址:vuex-shopcart的github地址
本文一边讲解如何实现购物车功能,一边带你如何查看vuex的文档
- 先展示下项目的目录结构(具体的细节不做过多解释)
具体的操作步骤如下(bootstrap的布局)
- product组件(纯静态代码)
<template>
<div class="product">
<h4>商品信息</h4>
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>id</th>
<th>名称</th>
<th>价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for='(shop,index) in shoplist' :key='index'>
<td>{{shop.id}}</td>
<td>{{shop.name}}</td>
<td>{{shop.price}}</td>
<td>
<div class="btn btn-info">购物车</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'product',
data() {
return {
shoplist: [
{
id: 11,
name: '鱼香肉丝',
price: 12,
},
{
id: 22,
name: '宫保鸡丁',
price: 14,
},
{
id: 34,
name: '土豆丝',
price: 10,
},
{
id: 47,
name: '米饭',
price: 2,
},
],
};
},
};
</script>
<style scoped>
.table-shop > th {
text-align: center;
}
.item-wrapper {
display: flex;
background-color: #dfdfdf;
align-items: center;
justify-content: center;
}
.item {
flex: 1;
}
</style>
- cart组件(纯静态代码)
<template>
<div>
<h4>已选商品</h4>
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>id</th>
<th>名称</th>
<th>价格</th>
<th>数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for='(shop,index) in cartProducts' :key='index'>
<td>{{shop.id}}</td>
<td>{{shop.name}}</td>
<td>{{shop.price}}</td>
<td>{{shop.num}}</td>
<td>
<div @click='delProduct(shop)' class="btn btn-danger btn-sm">删除</div>
</td>
</tr>
<tr v-if="!cartProducts.length">
<td colspan="5" class="text-center">您的购物车空空如也。。。</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'cart',
data() {
return {
cartProducts: [],
};
},
};
</script>
- info组件(纯静态代码)
<template>
<div>
<div class="item-wrapper">
<div class='item'>总数:
<strong>{{totalNum}}</strong>
</div>
<div class='item'>总价:
<strong>{{totalPrice}}</strong>
</div>
<div class="item btn btn-danger">清空购物车</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'info',
data() {
return {
totalNum: 13,
totalPrice: 342,
};
},
};
</script>
<style scoped>
.item-wrapper {
display: flex;
background-color: #dfdfdf;
align-items: center;
justify-content: center;
}
.item {
flex: 1;
}
</style>
</style>
- 完成以上的三个组件,现在要开始调用这些组件,在App.vue中调用
<template>
<div id="app">
<h3>Vuex购物车demo</h3>
<!-- 商品的列表 -->g
<product></product>
<!-- 购物车的列表 -->
<cart></cart>
<!-- 总数量价格 -->
<info></info>
</div>
</template>
<script>
import product from './components/product'; //商品的列表组件
import cart from './components/cart'; //已选商品的组件
import info from './components/info'; //总数量价格的组件
export default {
name: 'app',
components: {
product,
cart,
info,
},
};
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
如果你的姿势正确的话,会出现这个画面
解释这里为什么要分开组件的编写,vuex是就是存储的数据的中心的,每个组件都是拿到数据,这样才能vuex的价值(每个组件的代码不做解释,既然是学vuex,前提的一定有一些的vue的基础的)
安装vuex
npm install vuex --save
使用vuex ,搭建的vuex的目录结构,文章的目录结构,在你的项目中创建相应的文件,文件名称对应上,可参考官网项目结构
store/index.js
// 组装模块并导出 store 的地方
import Vue from 'vue';
import Vuex from 'vuex';
import cart from './modules/cart’; //购物车
Vue.use(Vuex);
export default new Vuex.Store({
modules: { //和文件名字对应
cart,
},
strict: process.env.NODE_ENV !== 'production', // 严格模式
});
- modules/cart.js
//初始化数据
const state = {};
//getter 抛出去的数据
const getters = {};
//action 异步的操作
const actions = {};
//mutation
const mutations = {};
export default {
state,
mutations,
actions,
getters,
};
- 搭建问vuex的我们现在要使用vuex的,在main.js中使用
import Vue from 'vue';
import App from './App';
import store from './store’; //引入
new Vue({
el: '#app',
store, //使用
template: '<App/>',
components: {
App,
},
});
到这步,vuex的模板基本,但是没有效果怎么办,现在开始修改的product.vue中data中的shoplist剪切到cart.js中的state中,
这里解释一下,state和组件中大data作用是一样,都是的设置假数据的和变量,查看文档的state修改cart.js的state
const state = {
//商品列表
shop_list: [{
id: 11,
name: '鱼香肉丝',
price: 12,
}, {
id: 22,
name: '宫保鸡丁',
price: 14
}, {
id: 34,
name: '土豆丝',
price: 10
}, {
id: 47,
name: '米饭',
price: 2
}],
//添加到购物车的商品(已选商品)
added:[]
}
- 既然state和data的作用是一样的,那么我们在组件中拿到state中的shop_list的数据呢?
- 这是需要getters的处理哦
//getter 抛出去的数据
const getters = {
//商品列表
shoplist:state => state.shop_list,
}
组件中怎么拿到这个shoplist数据呢?查看文档getters 中的mapGetters辅助函数的方法
这个数据是从product组件剪切走的,现在还回去,是这样秀的
script标签里面引入vuex的辅助函数, export default 中computed使用
import {mapGetters} from "vuex";
//第一种方法:vuex和组件中使用的shoplist的名称,下面所有其他的辅助函数都使用第一种方法,第二种方法不在说明
computed: {
...mapGetters(['shoplist’]) //这是数组['A’,’B’,...]
},
//第二种写法,vuex中抛出的shoplist,但是我们组件实际用的是shoplistData,实际就是换个名字
//computed: {
//…mapGetters({
// shoplistData:'shoplist’ //这是对象
//})
//},
上面的2中写法文档中有详细解释,希望先查看文档,这样看的比较好。
现在需要我们添加的购物车的操作,product.vue中按钮处添加
<div @click='addToCart(shop)' class="btn btn-info">购物车</div>
- 现在编写addToCart的方法,我们会到cart,js 中的action里面去,查看文档action的介绍,需要commit来分发actions
const actions = {
//添加到购物车操作
addToCart({commit},product){
commit('add’,{ //传递一个add的方法,携带参数id
id:product.id
})
},
}
- 既然我们分发出来了一个add的方法,我们在mutations中接受,mutation中可以直接拿到state里面所有的数据,因为这里的added是自己定一个已选商品的数组,我组件中点击时传递一个对象过来的,find这个对象,但是这里有2种情况,一个added为空或者有数据,但是不是当前点击的对象的数据,当为空时,我们人为个这个对象push一个id和num为1的值,有点击就是当前这个对象的时候,我们执行++的操作,这里打印一下的值,或者从vue-tool的种查看的,vuex的一个浏览器的查看,自己百度去安装,查看数据比较方便
const mutations = {
//添加到购物车操作
add(state,{id}){ //解构id 你可以 测试id 和 {id}的区别
let record = state.added.find(n=>n.id == id);
if(!record){
state.added.push({
id,
num:1
})
}else {
record.num++
}
console.info(record,state.added)
},
}
- 这里vuex中action的addToCart方法写好了,如何product.vue中的addToCart方法对应上去呢?
- 这里文档中有介绍,有个mapActions的辅助函数,
script中标签引入 methods中调用
import {mapGetters, mapActions } from ‘vuex’ //之前引入了一个mapGetters
methods: {
...mapActions(['addToCart'])
}
- 这样我们就是action和组件中方法关联起来了,
- 现在增加商品的click事件解决,但是我们要把added给抛出,但是前提要对added这个数组进行数据转化
- 修改getter中方法,添加一条
const getters = {
//商品列表
shoplist:state => state.shop_list,
//购物车的列表
cartProducts:state=>{
return state.added.map(({id,num})=>{ //在actions中只有的id和num的字段
//在原始数据数据上面进行刷选,这里通过id来匹配
let product = state.shop_list.find(n=>n.id == id)
// console.info('product',product)
return {
...product,
num
}
})
},
},
- 这里我们需要将cartProducts这个抛出去,和shoplist的方法一样的,只不过这个是在cart.vue中操作,
script中引入
import { mapGetters} from "vuex";
computed: {
...mapGetters(['cartProducts’]) //页面中v-for的数据
},
- 现在我们开始计算总价,总数量 和 清空购物车功能,在getter中操作,
const getters = {
//商品列表
shoplist:state => state.shop_list,
//购物车的列表
cartProducts:state=>{
return state.added.map(({id,num})=>{
let product = state.shop_list.find(n=>n.id == id)
// console.info('product',product)
return {
...product,
num
}
})
},
//计算总价
totalPrice:(state,getters)=>{ //getter中可以调用getter里面的方法,文档有介绍
let total = 0;
getters.cartProducts.forEach(n=>{
total += n.price * n.num
})
return total;
},
//计算总数量
totalNum:(state,getters)=>{
let total = 0;
getters.cartProducts.forEach(n=>{
total += n.num
})
return total;
},
}
- 调用的方法是一样,
import { mapGetters } from 'vuex'
computed:{
...mapGetters(['totalPrice','totalNum'])
}
- 清空购物车的操作,原理就是added的数组置为空数组就可以的,
<div class="item btn btn-danger" @click='clearAllCart'>清空购物车</div>
- 将actions中clearAllCart和info.vue中的组件联系到一起,前面已经介绍过了。cart.js中的actions
const actions = {
//添加到购物车操作
addToCart({commit},product){
commit('add',{
id:product.id
})
},
//清除购物车
clearAllCart({commit}){
commit('clearAll’) //分发一个clearAll事件,不带参数进行
},
}
- mutations接受
const mutations = {
//添加到购物车操作
add(state,{id}){
let record = state.added.find(n=>n.id == id);
if(!record){
state.added.push({
id,
num:1
})
}else {
record.num++
}
// console.info(record)
},
//清除购物车
clearAll(state){
state.added = []
},
}
- info.vue中调用clearAllCart方法,
import { mapGetters, mapActions } from 'vuex'
methods: {
...mapActions(['clearAllCart'])
},
- 还有一个小功能就是已选商品中的有个删除的按钮,实现这个功能,cart.vue组件中修改
<div @click='delProduct(shop)' class="btn btn-danger btn-sm">删除</div>
cart,js中actions
const actions = {
//添加到购物车操作
addToCart({commit},product){
commit('add',{
id:product.id
})
},
//清除购物车
clearAllCart({commit}){
commit('clearAll')
},
//删除购物车的指定的商品
delProduct({commit},product){
commit('del',product)//commit del的方法
}
}
- mutations
const mutations = {
//添加到购物车操作
add(state,{id}){
let record = state.added.find(n=>n.id == id);
if(!record){
state.added.push({
id,
num:1
})
}else {
record.num++
}
},
//清除购物车
clearAll(state){
state.added = []
},
//删除购物车的指定的商品
del(state,product){
//console.info(state,product)
state.added.forEach((n,i)=>{
if(n.id == product.id){
//console.info(11,n)
//找到下标值
state.added.splice(i,1)
}
})
},
}
- cart.vue调用
import { mapGetters,mapActions} from "vuex";
methods:{
...mapActions(['delProduct'])
}
到此为止,vuex的基本使用方法基本介绍完了,
1. 详细看看vuex中的例子和写法
2. 这个所有的actions的操作对于state中added数据都是共享的,这里修改其他地方也立即修改,都是同步的,特别明显就是不同组件中数据共享
3. 文章写的比较急,具体的查看的git的地址vuex-shopcart的github地址