vuex速记
本质: 全局状态(数据)管理工具
作用: 可以实现跨组件通信
五大核心模块:
- state:数据
- Mutation:修改数据(同步)
- Actions:处理异步行为
- Getters:计算属性
- Modules:数据分模块维护
五大核心模块
State
作用:声明数据
语法:
js
// 声明数据
state: {
// 属性名就是数据变量名,类似data函数
xxx: 值,
zzz: 值,
}
// 组件内,获取数据
computed: {
...mapState(['xxx', 'zzz'])
}
Mutation
作用:同步修改数据
语法:
js
// 定义mutations函数
mutations: {
// state是数据数据, payload是函数调用时传来的参数
xxx(state, payload){
}
}
// 获取mutations函数
methods: {
...mapMutation(['xxx', 'yyy'])
}
// 调用mutations函数-当做methods调用
this.xxx(参数)
this.yyy(参数)
Ations
作用:处理异步逻辑(发请求)
语法:
js
// 定义actions函数
mutations: {
xxx(state, payload){
}
}
actions: {
xxxAsync(context){
context.commit('xxx')
}
}
// 获取actions函数
methods: {
...mapActions(['xxxAsync'])
}
// 调用mutations函数-当做methods调用
this.xxxAsync(参数)
getters
作用:计算属性
js
// 声明数据
getters: {
计算属性名(state){
return ... // 一定要有返回值
}
}
// 组件内,获取计算属性
computed: {
...mapGetters(['计算属性名'])
}
阶段小结
js
new Vuex.Store({
state: {
xxx: 90
},
mutations: {
updateXxx(state, payload){
state.xxx = payload
}
},
actions: {
async updateXxxAsync(context, payload){
const res = await axios();
context.commit('updateXxx', res)
}
},
getters: {
doubleXxx(state){
return state.xxx * 2
}
}
});
vue
<script>
export default {
computed: {
...mapState(['count']),
...mapGetters(['doubleCount'])
},
methods: {
...mapMutations(['updateXxx']),
...mapActions(['updateXxxAsync'])
}
}
</script>
modules
作用:把数据和方法,放在不同的属性下
语法:
js
new Vuex.Store({
模块名: {
// 开启命名空间
namespaced: true,
state: {
xxx: 90
},
mutations: {
updateXxx(state, payload){
state.xxx = payload
}
},
actions: {
async updateXxxAsync(context, payload){
const res = await axios();
context.commit('updateXxx', res)
}
},
getters: {
doubleXxx(state){
return state.xxx * 2
}
}
}
});
vue
<script>
export default {
computed: {
...mapState('模块名', ['count']),
...mapGetters('模块名', ['doubleCount'])
},
methods: {
...mapMutations('模块名', ['updateXxx']),
...mapActions('模块名', ['updateXxxAsync'])
}
}
</script>
综合案例 - 购物车
创建项目
- 脚手架新建项目 (注意:勾选vuex)
vue create vue-cart-demo
- 将原本src内容清空,替换成教学资料的《vuex-cart-准备代码》
需求:
- 发请求动态渲染购物车,数据存vuex (存cart模块, 将来还会有user模块,article模块...)
- 可以修改数据
- 动态计算总价和总数量
构建vuex-cart模块
- 新建
store/modules/cart.js
jsx
export default {
namespaced: true,
state () {
return {
list: []
}
},
}
- 挂载到 vuex 仓库上
store/index.js
jsx
import Vuex from 'vuex'
import Vue from 'vue'
import cart from './modules/cart'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
cart
}
})
export default store
准备后端接口服务环境(了解)
- 安装全局工具 json-server (全局工具仅需要安装一次)
yarn global add json-server 或 npm i json-server -g
- 代码根目录新建一个 db 目录
- 将资料 index.json 移入 db 目录
- 进入 db 目录,执行命令,启动后端接口服务
json-server index.json
请求动态渲染数据
jsx
// 获取接口
// 请求方式:get
// 请求地址:http://localhost:3000/cart
- 安装 axios
jsx
yarn add axios
- 准备actions 和 mutations
jsx
import axios from 'axios'
export default {
namespaced: true,
state () {
return {
list: []
}
},
mutations: {
updateList (state, payload) {
state.list = payload
}
},
actions: {
async getList (ctx) {
const res = await axios.get('http://localhost:3000/cart')
ctx.commit('updateList', res.data)
}
}
}
App.vue
页面中调用 action, 获取数据
jsx
import { mapState } from 'vuex'
export default {
name: 'App',
components: {
CartHeader,
CartFooter,
CartItem
},
created () {
this.$store.dispatch('cart/getList')
},
computed: {
...mapState('cart', ['list'])
}
}
- 动态渲染
jsx
<!-- 商品 Item 项组件 -->
<cart-item v-for="item in list" :key="item.id" :item="item"></cart-item>
cart-item.vue
jsx
<template>
<div class="goods-container">
<!-- 左侧图片区域 -->
<div class="left">
<img :src="item.thumb" class="avatar" alt="">
</div>
<!-- 右侧商品区域 -->
<div class="right">
<!-- 标题 -->
<div class="title">{{item.name}}</div>
<div class="info">
<!-- 单价 -->
<span class="price">¥{{item.price}}</span>
<div class="btns">
<!-- 按钮区域 -->
<button class="btn btn-light">-</button>
<span class="count">{{item.count}}</span>
<button class="btn btn-light">+</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CartItem',
props: {
item: Object
},
methods: {
}
}
</script>
修改数量
jsx
请求方式:patch
请求地址:http://localhost:3000/cart/:id值 表示修改的是哪个对象
请求参数:
{
name: '新值', 【可选】
price: '新值', 【可选】
count: '新值', 【可选】
thumb: '新值' 【可选】
}
- 注册点击事件
jsx
<!-- 按钮区域 -->
<button class="btn btn-light" @click="onBtnClick(-1)">-</button>
<span class="count">{{item.count}}</span>
<button class="btn btn-light" @click="onBtnClick(1)">+</button>
- 页面中dispatch action
jsx
methods: {
...mapActions('cart', ['updateCountAsync'])
}
onBtnClick (step) {
const newCount = this.item.count + step
if (newCount < 1) return
// 发送修改数量请求
this.updateCountAsync({
id: this.item.id,
count: newCount
})
}
- 提供action函数
jsx
async updateCountAsync (ctx, payload) {
await axios.patch('http://localhost:3000/cart/' + payload.id, {
count: payload.count
})
ctx.commit('updateCount', payload)
}
- 提供mutation处理函数
jsx
mutations: {
...,
updateCount (state, payload) {
const goods = state.list.find((item) => item.id === payload.id)
goods.count = payload.count
}
},
底部总价展示
- 提供getters
jsx
getters: {
total(state) {
return state.list.reduce((p, c) => p + c.count, 0);
},
totalPrice (state) {
return state.list.reduce((p, c) => p + c.count * c.price, 0);
},
},
- 动态渲染
jsx
<template>
<div class="footer-container">
<!-- 中间的合计 -->
<div>
<span>共 {{total}} 件商品,合计:</span>
<span class="price">¥{{totalPrice}}</span>
</div>
<!-- 右侧结算按钮 -->
<button class="btn btn-success btn-settle">结算</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'CartFooter',
computed: {
...mapGetters('cart', ['total', 'totalPrice'])
}
}
</script>