Vuex 的应用与购物车功能实现
# 1. vuex基本使用
# 1.1 vuex基本介绍
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
# 1.2 配置 vuex
在项目根目录中创建
store
文件夹,专门用来存放 vuex 相关的模块在
store
目录上鼠标右键,选择新建 -> js文件
,新建store.js
文件:在
store.js
中按照如下 4 个步骤初始化 Store 的实例对象:// 1. 导入 Vue 和 Vuex import Vue from 'vue' import Vuex from 'vuex' // 2. 将 Vuex 安装为 Vue 的插件 Vue.use(Vuex) // 3. 创建 Store 的实例对象 const store = new Vuex.Store({ // TODO:挂载 store 模块 modules: {}, }) // 4. 向外共享 Store 的实例对象 export default store
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15在
main.js
中导入store
实例对象并挂载到 Vue 的实例上:// 1. 导入 store 的实例对象 import store from './store/store.js' // 省略其它代码... const app = new Vue({ ...App, // 2. 将 store 挂载到 Vue 实例上 store, }) app.$mount()
1
2
3
4
5
6
7
8
9
10
11
# 1.3 定义数据和使用数据
通过state定义数据
const store = new Vuex.Store({ // TODO:挂载 store 模块 modules: {}, state: { test: '测试' } })
1
2
3
4
5
6
7在页面中通过计算属性使用数据
computed: { test() { return this.$store.state.test } },
1
2
3
4
5页面中使用数据
{{test}}
1
# 1.4 vuex中模块的概念
- 可以通过modules定义模块
- 模块中包含state、mutation、action、getters
# 2. 添加购物车功能
# 2.1 新建cart模块
在
store
目录上鼠标右键,选择新建 -> js文件
,创建购物车的 store 模块,命名为cart.js
:在
cart.js
中,初始化如下的 vuex 模块:export default { // 为当前模块开启命名空间 namespaced: true, // 模块的 state 数据 state: () => ({ // 购物车的数组,用来存储购物车中每个商品的信息对象 // 每个商品的信息对象,都包含如下 6 个属性: // { goods_id, goods_name, goods_price, goods_count, goods_small_logo, goods_state } cart: [], }), // 模块的 mutations 方法 mutations: {}, // 模块的 getters 属性 getters: {}, }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18在
store/store.js
模块中,导入并挂载购物车的 vuex 模块,示例代码如下:import Vue from 'vue' import Vuex from 'vuex' // 1. 导入购物车的 vuex 模块 import moduleCart from './cart.js' Vue.use(Vuex) const store = new Vuex.Store({ // TODO:挂载 store 模块 modules: { // 2. 挂载购物车的 vuex 模块,模块内成员的访问路径被调整为 m_cart,例如: // 购物车模块中 cart 数组的访问路径是 m_cart/cart m_cart: moduleCart, }, }) export default store
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.2 实现加入购物车的功能
在 store 目录下的
cart.js
模块中,封装一个将商品信息加入购物车的 mutations 方法,命名为addToCart
。示例代码如下:export default { // 为当前模块开启命名空间 namespaced: true, // 模块的 state 数据 state: () => ({ // 购物车的数组,用来存储购物车中每个商品的信息对象 // 每个商品的信息对象,都包含如下 6 个属性: // { goods_id, goods_name, goods_price, goods_count, goods_small_logo, goods_state } cart: [], }), // 模块的 mutations 方法 mutations: { addToCart(state, goods) { // 根据提交的商品的Id,查询购物车中是否存在这件商品 // 如果不存在,则 findResult 为 undefined;否则,为查找到的商品信息对象 const findResult = state.cart.find((x) => x.goods_id === goods.goods_id) if (!findResult) { // 如果购物车中没有这件商品,则直接 push state.cart.push(goods) } else { // 如果购物车中有这件商品,则只更新数量即可 findResult.goods_count++ } }, }, // 模块的 getters 属性 getters: {}, }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32在商品详情页面中,通过
mapMutations
这个辅助方法,把 vuex 中m_cart
模块下的addToCart
方法映射到当前页面:// 按需导入 mapMutations 这个辅助方法 import { mapMutations } from 'vuex' export default { methods: { // 把 m_cart 模块中的 addToCart 方法映射到当前页面使用 ...mapMutations('cart', ['addToCart']), }, }
1
2
3
4
5
6
7
8
9为商品导航组件
uni-goods-nav
绑定@buttonClick="buttonClick"
事件处理函数:// 右侧按钮的点击事件处理函数 buttonClick() { // 1. 组织一个商品的信息对象 const goods = { goods_id: this.goods_info.goods_id, // 商品的Id goods_name: this.goods_info.goods_name, // 商品的名称 goods_price: this.goods_info.goods_price, // 商品的价格 goods_count: 1, // 商品的数量 goods_small_logo: this.goods_info.goods_small_logo, // 商品的图片 goods_state: true // 商品的勾选状态 } // 2. 通过 this 调用映射过来的 addToCart 方法,把商品信息对象存储到购物车中 this.addToCart(goods) },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2.3 使用createlogger插件
- 引入createlogger
import Vuex, {
createLogger
} from 'vuex'
2
3
- 启用插件
const store = new Vuex.Store({
// TODO:挂载 store 模块
modules: {
cart: moduleCart
},
plugins: process.env.NODE_ENV !== 'production' ? [createLogger()] : []
})
2
3
4
5
6
7
# 2.4 统计购物车中商品的总数量
在
cart.js
模块中,在getters
节点下定义一个total
方法,用来统计购物车中商品的总数量:// 模块的 getters 属性 getters: { // 统计购物车中商品的总数量 total(state) { return state.carts.length } }
1
2
3
4
5
6
7在商品详情页面的
script
标签中,按需导入mapGetters
方法并进行使用:// 按需导入 mapGetters 这个辅助方法 import { mapGetters } from 'vuex' export default { computed: { // 把 cart 模块中名称为 total 的 getter 映射到当前页面中使用 ...mapGetters('cart', ['total']), }, }
1
2
3
4
5
6
7
8
9动态为购物车按钮的徽标赋值:
<van-goods-action-icon :info="total" icon="cart-o" text="购物车" bind:click="onClickIcon" />
1
# 2.5 持久化存储购物车中的商品
引入saveToStorage
export default { mutations: { addToCart(state, goods) { // 根据提交的商品的Id,查询购物车中是否存在这件商品 // 如果不存在,则 findResult 为 undefined;否则,为查找到的商品信息对象 const findResult = state.cart.find((x) => x.goods_id === goods.goods_id) if (!findResult) { // 如果购物车中没有这件商品,则直接 push state.cart.push(goods) } else { // 如果购物车中有这件商品,则只更新数量即可 findResult.goods_count++ } uni.setStorageSync('cart', state.cart) }, } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19修改
cart.js
模块中的state
函数,读取本地存储的购物车数据,对 cart 数组进行初始化:// 模块的 state 数据 state: () => ({ // 购物车的数组,用来存储购物车中每个商品的信息对象 // 每个商品的信息对象,都包含如下 6 个属性: // { goods_id, goods_name, goods_price, goods_count, goods_small_logo, goods_state } cart: uni.getStorageSync('cart') || [] }),
1
2
3
4
5
6
7
# 3. 动态设置tabbar数字
# 3.1 tabBar 页面设置数字徽标
需求描述:从商品详情页面导航到购物车页面之后,需要为 tabBar 中的购物车动态设置数字徽标。
把 Store 中的 total 映射到
cart.vue
中使用:// 按需导入 mapGetters 这个辅助方法 import { mapGetters } from 'vuex' export default { data() { return {} }, computed: { // 将 m_cart 模块中的 total 映射为当前页面的计算属性 ...mapGetters('m_cart', ['total']), }, }
1
2
3
4
5
6
7
8
9
10
11
12在页面刚显示出来的时候,立即调用
setBadge
方法,为 tabBar 设置数字徽标:onShow() { // 在页面刚展示的时候,设置数字徽标 this.setBadge() }
1
2
3
4在
methods
节点中,声明setBadge
方法如下,通过uni.setTabBarBadge()
为 tabBar 设置数字徽标:methods: { setBadge() { // 调用 uni.setTabBarBadge() 方法,为购物车设置右上角的徽标 uni.setTabBarBadge({ index: 2, // 索引 text: this.total + '' // 注意:text 的值必须是字符串,不能是数字 }) } }
1
2
3
4
5
6
7
8
9
# 3.2 将设置 tabBar 徽标的代码抽离为 mixins
注意:除了要在 cart.vue 页面中设置购物车的数字徽标,还需要在其它 3 个 tabBar 页面中,为购物车设置数字徽标。
此时可以使用 Vue 提供的 mixins (opens new window) 特性,提高代码的可维护性。
在项目根目录中新建
mixins
文件夹,并在mixins
文件夹之下新建tabbar-badge.js
文件,用来把设置 tabBar 徽标的代码封装为一个 mixin 文件:import { mapGetters } from 'vuex' // 导出一个 mixin 对象 export default { computed: { ...mapGetters('cart', ['total']), }, onShow() { // 在页面刚展示的时候,设置数字徽标 this.setBadge() }, methods: { setBadge() { // 调用 uni.setTabBarBadge() 方法,为购物车设置右上角的徽标 uni.setTabBarBadge({ index: 2, text: this.total + '', // 注意:text 的值必须是字符串,不能是数字 }) }, }, }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21修改
home.vue
,cate.vue
,cart.vue
,my.vue
这 4 个 tabBar 页面的源代码,分别导入@/mixins/tabbar-badge.js
模块并进行使用:// 导入自己封装的 mixin 模块 import badgeMix from '@/mixins/tabbar-badge.js' export default { // 将 badgeMix 混入到当前的页面中进行使用 mixins: [badgeMix], // 省略其它代码... }
1
2
3
4
5
6
7
8