在uni-app中使用Vuex

引入vuex

uni-app已经内置了vuex,我们可以直接引入使用。官方文档

state的使用

我们按照大型项目中的实际需求,将不同模块的状态管理划分为不同的module。例如我们有2个模块home和user,将home和user模块引入到index中(此处只是分步骤讲解,可以直接下载Demo)。

此时我们在home模块中定义state:

    state: {
        homeCount: 123,
        homeName: '刘德华'
    }

我们想要在场景中获取内容:

//普通方式获取模块home中的homeName的内容
console.log(this.$store.state.home.homeName)

通过辅助函数mapState获取(同时还可以重新命名变量):

            ...mapState( {
                    // 映射 this.myCount 为 this.$store.state.home.homeCount
                    myCount: state => state.home.homeCount,
                    myName: state => state.home.homeName
                }),

Getters

Getters可以认为是 store 的计算属性。我的理解是相当于computed的角色。

此时我们在home模块中定义getters:

    getters: { //(可以认为是 store 的计算属性)
        homeCountPower: state => state.homeCount * state.homeCount,
        homeNameDesc: state => state.homeName + '长得真帅啊',
    },

我们想要在场景中获取内容:

// 获取getters的内容
 console.log(this.$store.getters.homeNameDesc)

 //error:注意此处不能通过.home.getters,而是直接.getters
 // console.log(this.$store.home.getters.homeNameDesc)

通过辅助函数mapGetters获取(同时还可以重新命名变量):

            ...mapGetters(
                 {
                    countPower: 'homeCountPower',
                    nameDesc: 'homeNameDesc'
                }
            ),

当然,我们也可以不通过getters来在对应的模块来组织内容,我们可以根据实际需要,直接在computed中来更改内容也是可以的:

        computed: {
            homeCount() {
                return this.$store.state.home.homeCount + 'KG'
            },
            }

getters中的函数是可以相互调用的:

    getters: { //(可以认为是 store 的计算属性)
        homeCountPower: state => state.homeCount * state.homeCount,
        homeNameDesc: state => state.homeName + '长得真帅啊',
        homeInfo: (state, getters) => {
            //假如是复杂的处理逻辑,可以用上面的简便写法
            return '明星' + getters.homeNameDesc + '!!!!!!!'
        } 
    },

在主页中使用homeInfo:

//如果不更改getters中原有的名字,可以直接传一个数组
            ...mapGetters(
                 ['homeInfo']
            ),

getters中的函数也是可以传递参数的。假如现在我需要动态给homeCount进行更改,需要这要做:

        editHomeCount: (state) => {//动态更改homeCount
            return (data) => {
                return state.homeCount + data 
            }
        } 
            //如果不更改getters中原有的名字,可以直接传一个数组
            ...mapGetters(
                 ['homeInfo', 'editHomeCount']
            ),
        <view class="text-area">
            <text class="title">{{homeInfo}}</text>
        </view>
        <view class="text-area">
            <text class="title">{{editHomeCount(1000)}}</text>
        </view>

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。言下之意就是我们规范的修改store中的变量只能通过commit的 方式。

    mutations: {
        [ADD](state, num) {
            state.homeCount += num
        },
        [MIN](state, num) {
            state.homeCount -= num
        }
    },

ADD 和MIN是通过export导出的常量。根据ES6的语法,来替代函数名称。

使用辅助函数mapMutations:

            //方式1:直接使用
            ...mapMutations(
                [ADD, MIN],
            ),
            //方式2: 重新命名
            ...mapMutations( {
                addAmount: ADD,
                minAmount: MIN
            }),

此时我们呢用2个按钮来定义点击事件:

<button type="primary" @click="add(1000)">增加</button>
<button type="primary" @click="min(2000)">减小</button>

我们有3种方式来调用mutations的函数:

            add(data) {
                //方式1:
                this.ADD(data)
                //方式2:
                // this.addAmount(data)
                //方式3:直接通过commit方式提交
                // this.$store.commit(ADD, data)
            },
            min(data) {
                this.MIN(data)
                // this.minAmount(data)
                // this.$store.commit(MIN, data)
            },

如果我们传给mutations中函数的参数比较复杂呢?我们在state中增加一个数组:

        homeGoods: [{
                goodsId: 100,
                goodsName: '番茄',
                goodsPrice: 22
            },
            {
                goodsId: 100,
                goodsName: '土豆',
                goodsPrice: 44
            }
        ]

mutations中增加一个添加商品的函数:

        addGoods(state, playload) {//增加一个商品
            //方式1和2的方式:
            // state.homeGoods.push(playload)
            //方式3:
            state.homeGoods.push(playload.data)
        }
<button type="primary" @click="addMoreGoods">增加一个商品</button>

            addMoreGoods() {
                let goods = {
                    goodsId: 100,
                    goodsName: '榴莲',
                    goodsPrice: 150
                }
                //方式1:
                // this.addGoods(goods)
                //方式2:
                // this.$store.commit('addGoods', goods)
                //方式3:(需要更改mutations中的取参数的方式)
                this.$store.commit({
                    type: 'addGoods',
                    data: goods 
                })
                console.log(this.$store.state.home.homeGoods)
            },
Action

Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。

action是用来针对异步操作的。有的人会把网络请求在action中实现,然后在页面中直接调用。页面的代码会减少很多。

    actions: {
        requestListData(context) {
            let goods = {
                goodsId: 120,
                goodsName: '西瓜',
                goodsPrice: 66
            }
            // 模拟异步操作
            setTimeout(() => {
                context.commit({
                    type: 'addGoods',
                    data: goods 
                })
            },2000)
        }
    }

在页面中使用:

<button type="primary" @click="asyncAddMoreGoods">异步增加一个商品</button>


            ...mapActions({
                requestData: 'requestListData'
            }),
            asyncAddMoreGoods() {
                //方式1:
                // this.requestData()
                // 方式2:
                this.$store.dispatch('requestListData')
            },

有的时候需要异步增加结果回调,我们可以这样:

actions: {
            requestHomeInfo(context,playload) {
            // 模拟异步操作
            setTimeout(() => {
                context.commit('addOneGoods',playload.data)
                playload.callback('添加成功了。。')
            },2000)
        }
}

在页面中使用callback回调:

<button type="primary" @click="asyncCallback">异步增加商品回调</button>


            asyncCallback() {
                //直接使用方式2进行测试:
                let goods = {
                    goodsId: 199,
                    goodsName: '圣女果',
                    goodsPrice: 177
                }
                this.$store.dispatch('requestHomeInfo', {
                    data: goods,
                    callback: (res) => {
                        console.log(res)
                    }
                })
            },

使用promise来实现:

actions: {
            requestPromiseInfo(context, playload) {
            return new Promise((resolve, reject) => {
                // 模拟异步操作
                setTimeout(() => {
                    context.commit('addOneGoods',playload)
                    resolve('Promise添加成功了...')
                },2000)
            })
        }
}
            asyncPromise() {
                //直接使用方式2进行测试:
                let goods = {
                    goodsId: 911,
                    goodsName: '酸奶',
                    goodsPrice: 199
                }
                this.$store.
                        dispatch('requestPromiseInfo', goods)
                        .then(res => {
                            console.log(res)
                        }) 
            },

在uni-APP中使用Vuex案例

home.js

//home.js
import {
    ADD,
    MIN
} from '../common/const.js'


export default {
    state: {
        homeCount: 123,
        homeName: '刘德华',
        homeGoods: [{
                goodsId: 100,
                goodsName: '番茄',
                goodsPrice: 22
            },
            {
                goodsId: 100,
                goodsName: '土豆',
                goodsPrice: 44
            }
        ]
    },
    getters: { //(可以认为是 store 的计算属性)
        homeCountPower: state => state.homeCount * state.homeCount,
        homeNameDesc: state => state.homeName + '长得真帅啊',
        homeInfo: (state, getters) => {
            //假如这是复杂的处理逻辑,简单的处理可以用上面的简便写法
            return '明星' + getters.homeNameDesc + '!!!!!!!'
        },
        editHomeCount: (state) => {//动态更改homeCount
            return (data) => {
                return state.homeCount + data 
            }
        } 
    },
    mutations: {
        [ADD](state, num) {
            state.homeCount += num
        },
        [MIN](state, num) {
            state.homeCount -= num
        },
        addGoods(state, playload) {//增加一个商品
            //方式1和2的方式:
            // state.homeGoods.push(playload)
            //方式3:
            state.homeGoods.push(playload.data)
        },
        addOneGoods(state, playload) {//增加一个商品
            state.homeGoods.push(playload)
        },
    },
    actions: {
        requestListData(context) {
            let goods = {
                goodsId: 120,
                goodsName: '西瓜',
                goodsPrice: 66
            }
            // 模拟异步操作
            setTimeout(() => {
                context.commit({
                    type: 'addGoods',
                    data: goods 
                })
            },2000)
        },
        requestHomeInfo(context,playload) {
            // 模拟异步操作
            setTimeout(() => {
                context.commit('addOneGoods',playload.data)
                playload.callback('添加成功了。。')
            },2000)
        },
        requestPromiseInfo(context, playload) {
            return new Promise((resolve, reject) => {
                // 模拟异步操作
                setTimeout(() => {
                    context.commit('addOneGoods',playload)
                    resolve('Promise添加成功了...')
                },2000)
            })
        }
    }
}

user.js

import {
    USER_NAME,
    USER_AGE
} from '../common/const.js'

export default {
    state: {
        userAge: 18,
        userName: '渣渣辉'
    },
    getters: {
        userAge: state => state.userAge + '岁!',
        userName: state => state.userName + '贪玩蓝月!',
    },
    mutations: {
        [USER_NAME](state, name) {
            state.userName = name
        },
        [USER_AGE](state, age) {
            state.userAge = age
        }
    },
    actions: {

    }
}

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import home from'./home.js'
import user from'./user.js'
Vue.use(Vuex)

// home使用了命名空间
const store = new Vuex.Store({
    modules:{
        home,
        user 
    }
})
export default store

创建好对应的文件后,我们需要到uni-APP的main文件中进行挂载:

iimport Vue from 'vue'
import App from './App'
// 1.导入
import store from './store'
Vue.config.productionTip = false

App.mpType = 'app'
const app = new Vue({
    ...App,
    store
})
app.$mount()

在我们需要的vue文件中使用:

vuex为我们提供了mapState mapMutations mapGetters mapActions等辅助函数。可以根据自己的实际情况来决定是否使用。我将分别演示使用与否的情况。

index.vue

<template>
    <view class="content">
        <view class="text-area">
            <block v-for="item in homeGoodsList " :key='item.goodsId'>
                <view>
                    <text class="title">{{item.goodsId}}</text>
                    <text class="title">{{item.goodsName}}</text>
                    <text class="title">{{item.goodsPrice}}</text>
                </view>
            </block>
        </view>
        <view class="text-area">
            <text class="title">{{myName}}</text>
        </view>
        <view class="text-area">
            <text class="title">{{myCount}}</text>
        </view>
        <view class="text-area">
            <text class="title">{{nameDesc}}</text>
        </view>
        <view class="text-area">
            <text class="title">{{countPower}}</text>
        </view>
        <view class="text-area">
            <text class="title">{{homeInfo}}</text>
        </view>
        <view class="text-area">
            <text class="title">{{editHomeCount(1000)}}</text>
        </view>

        <!-- ***************************************** -->
        <view class="text-area-user">
            <text class="title">{{userName}}</text>
        </view>
        <view class="text-area-user">
            <text class="title">{{userAge}}</text>
        </view>
        <view class="text-area-user">
            <text class="title">{{userName1}}</text>
        </view>
        <view class="text-area-user">
            <text class="title">{{userAge1}}</text>
        </view>
        <view class="text-content">
            <view>
                <button type="primary" @click="add(1000)">增加</button>
                <button type="primary" @click="min(2000)">减小</button>
                <button type="primary" @click="addMoreGoods">增加一个商品</button>
                <button type="primary" @click="asyncAddMoreGoods">异步增加一个商品</button>
                <button type="primary" @click="asyncCallback">异步callback增加商品回调</button>
                <button type="primary" @click="asyncPromise">异步promise增加商品回调</button>
                <button type="warn" @click="gotoUser">跳转</button>
                <button type="warn" @click="changeName">改变名字</button>
                <button type="warn" @click="changeAge">改变年龄</button>
            </view>
        </view>
    </view>
</template>

<script>
    import {
        ADD,
        MIN,
        USER_NAME,
        USER_AGE
    } from '../../common/const.js';
    import {
        mapState,
        mapMutations,
        mapGetters,
        mapActions
    } from 'vuex';

    export default {
        data() {
            return {

            }
        },
        computed: {
            homeCount() {
                return this.$store.state.home.homeCount + 'KG'
            },
            ...mapGetters({
                countPower: 'homeCountPower',
                nameDesc: 'homeNameDesc',
            }),
            //如果不更改getters中原有的名字,可以直接传一个数组
            ...mapGetters(
                ['homeInfo', 'editHomeCount']
            ),
            ...mapState({
                // 映射 this.count 为 this.$store.state.home.homeCount
                myCount: state => state.home.homeCount,
                myName: state => state.home.homeName,
                homeGoodsList: state => state.home.homeGoods
            }),

            //不使用命名空间的user
            userAge() {
                return this.$store.state.user.userAge
            },
            userName() {
                return this.$store.state.user.userName
            },
            userAge1() {
                return this.$store.getters.userAge
            },
            userName1() {
                return this.$store.getters.userName
            }
        },
        onLoad() {
            //普通方式获取模块home中的homeName的内容
            console.log(this.$store.state.home.homeName)
            // 获取getters的内容
            console.log(this.$store.getters.homeNameDesc)
            //error:注意此处不能通过.home.getters,而是直接.getters
            // console.log(this.$store.home.getters.homeNameDesc)



            // console.log(this.$store.state.user.userAge)
            // console.log(this.$store.getters.userName)
            // console.log(this.$store.getters.userAge)
        },
        methods: {
            //将this.addAmount(data) 映射为 this.$store.commit(ADD, data)
            //将this.minAmount(data) 映射为 this.$store.commit(MIN, data)
            //同理,mapActions也可以如此使用
            //注: 如果调用名一致,则可以直接使用数组,而不是对象:

            //方式1:直接使用
            ...mapMutations(
                [ADD, MIN],
            ),
            //方式2: 重新命名
            ...mapMutations({
                addAmount: ADD,
                minAmount: MIN
            }),
            ...mapMutations(
                ['addGoods']
            ),
            ...mapActions({
                requestData: 'requestListData'
            }),
            asyncPromise() {
                //直接使用方式2进行测试:
                let goods = {
                    goodsId: 911,
                    goodsName: '酸奶',
                    goodsPrice: 199
                }
                this.$store.
                        dispatch('requestPromiseInfo', goods)
                        .then(res => {
                            console.log(res)
                        }) 
            },
            asyncCallback() {
                //直接使用方式2进行测试:
                let goods = {
                    goodsId: 199,
                    goodsName: '圣女果',
                    goodsPrice: 177
                }
                this.$store.dispatch('requestHomeInfo', {
                    data: goods,
                    callback: (res) => {
                        console.log(res)
                    }
                })
            },
            asyncAddMoreGoods() {
                //方式1:
                // this.requestData()
                // 方式2:
                this.$store.dispatch('requestListData')
            },
            addMoreGoods() {
                let goods = {
                    goodsId: 100,
                    goodsName: '榴莲',
                    goodsPrice: 150
                }
                //方式1:
                // this.addGoods(goods)
                //方式2:
                // this.$store.commit('addGoods', goods)
                //方式3:(需要更改mutations中的取参数的方式)
                this.$store.commit({
                    type: 'addGoods',
                    data: goods
                })
                console.log(this.$store.state.home.homeGoods)
            },
            add(data) {
                //方式1:
                this.ADD(data)
                //方式2:
                // this.addAmount(data)
                //方式3:直接通过commit方式提交
                // this.$store.commit(ADD, data)
            },
            min(data) {
                this.MIN(data)
                // this.minAmount(data)
                // this.$store.commit(MIN, data)
            },
            gotoUser() {
                uni.navigateTo({
                    url: '../user/user',
                    success: res => {},
                    fail: () => {},
                    complete: () => {}
                });
            },

            ...mapMutations({
                setUserAge: USER_AGE,
                setUserName: USER_NAME
            }),
            changeAge() {
                this.setUserAge(33)
            },
            changeName() {
                this.setUserName('张家辉')
            }
        }
    }
</script>

<style>
    .content {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }

    .logo {
        height: 200rpx;
        width: 200rpx;
        margin-top: 200rpx;
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 50rpx;
    }

    .text-area {
        display: flex;
        justify-content: center;
    }

    .text-area-user {
        display: flex;
        justify-content: center;
        background-color: #F0AD4E;
    }


    .title {
        font-size: 36rpx;
        color: #8f8f94;
    }

    .text-content {
        width: 100vw;
        background: #007AFF;
        height: 300upx;
    }
</style>

user.vue

<template>
    <view class="content">
        <view class="text-area">
            <text class="title">{{userName}}</text>
        </view>
        <view class="text-area">
            <text class="title">{{userAge}}</text>
        </view>

        <view class="text-area">
            <text class="title">{{userName1}}</text>
        </view>
        <view class="text-area">
            <text class="title">{{userAge1}}</text>
        </view>
        <view class="text-content">
            <view>
                <button type="warn" @click="gotoBack">返回上一页</button>
                <button type="warn" @click="changeUserName">改变名字</button>
                <button type="warn" @click="changeUserAge">改变年龄</button>
            </view>
        </view>
    </view>
</template>

<script>
    import {
        USER_NAME,
        USER_AGE
    } from '../../common/const.js';
    import {
        mapState,
        mapMutations,
        mapGetters,
        mapActions
    } from 'vuex';

    export default {
        data() {
            return {}
        },
        computed: {
            ...mapState({
                userAge1: state => state.user.userAge + '哈哈哈哈',
                userName1: state => state.user.userName + '真可爱啊啊啊'
            }),
            ...mapGetters([
                 // 把 `this.userAge` 映射为 `this.$store.getters.userAge`
                "userAge",
                "userName"
            ])
        },
        onLoad() {
            // 想要获取getters的内容,但是home是使用命名空间来获取的,所以暂时不知道怎么获取
            // console.log(this.$store.getters)
            console.log(this.$store.state.user.userAge)
            console.log(this.$store.state.user.userName)
        },
        methods: {
            ...mapMutations(
                [
                    USER_NAME, USER_AGE
                ]),
                changeUserName() {
                    this.USER_NAME('zhangjiahui')
                },
                changeUserAge() {
                    this.USER_AGE(77)
                },
                gotoBack() {
                    uni.navigateBack({
                        delta: 1
                    });
                }
        }
    }
</script>

<style>
    .content {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }

    .logo {
        height: 200rpx;
        width: 200rpx;
        margin-top: 200rpx;
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 50rpx;
    }

    .text-area {
        display: flex;
        justify-content: center;
    }

    .title {
        font-size: 36rpx;
        color: #8f8f94;
    }

    .text-content {
        width: 100vw;
        background: #007AFF;
        height: 300upx;
    }
</style>

Demo链接


   转载规则


《在uni-app中使用Vuex》 刘星星 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
在uni-app中自定义组件与插件 在uni-app中自定义组件与插件
前言在Vue的开发中,我们为了使代码能有更好的复用性,在开发阶段将具有相似业务的模块功能抽取成组件。而对于插件而言,则是通常用来为 Vue 添加全局功能。像我们使用的vue-router,就是一个插件。为了演示组件和插件的用法,我
2019-11-21
下一篇 
在uni-app中使用微信登录总结 在uni-app中使用微信登录总结
在uni-app中使用微信登录总结最近开发了一款使用uni-app框架的小程序,名字叫“花果鲜inside”(因为是针对公司业务的小程序,注册后需要后台审核,所以只能游客浏览哈)。现将开发问题中涉及到的微信授权登录业务总结一下. 登录
2019-09-01
  目录