引入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>