一、准备:vuex目录/文件构建
vuex文档:https://vuex.vuejs.org/zh/guide/
vuex实操:https://blog.csdn.net/u010132177/article/details/103744554
1.dos命令创建store文件夹/文件src/store
md store
cd store
echo export default{}>actions.js
echo export default{}>getters.js
echo export default{}> index.js
echo export default{}> mutation-types.js
echo export default{}> mutations.js
echo export default{}>state.js
store目录结构共6个文件
store/
actions.js
getters.js
index.js
mutation-types.js
mutations.js
state.js
二、vuex实战:管理代码
vuex各组件关系:
view ->Action(dispatch) ->Mutations(Commit) ->State(Mutate) -> View
1.state.js根据页面需要构建状态
// 所有要管理的状态数据:从页面需求分析出来,最好和api/index.js里的命名相同
export default{
latitude: 40.10038, // 纬度
longitude: 116.36867, // 经度
address: {}, //地址相关信息对象
categorys: [], // 食品分类数组
shops: [], // 商家数组
userInfo: {}, // 用户信息
goods: [], // 商品列表
ratings: [], // 商家评价列表
info: {}, // 商家信息
cartFoods: [], // 购物车中食物的列表
searchShops: [], // 搜索得到的商家列表
}
2.index.js 把各部分统一到一起,并new一个Vuex.Store对象
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import actions from './actions'
import mutations from './mutations'
import getters from './getters'
Vue.use(Vuex)
export default new Vuex.Store({
state,
actions,
mutations,
getters
})
3.mutation-types.js 定义mutation常量用于对其动态命名,以方便管理维护
/*
包含n个mutation的type名称常量
*/
export const RECEIVE_ADDRESS = 'receive_address' // 接收地址
export const RECEIVE_CATEGORYS = 'receive_categorys' // 接收食品分类数组
export const RECEIVE_SHOPS = 'receive_shops' // 接收商家数组
export const RECEIVE_USER_INFO = 'receive_user_info' // 接收用户信息
export const RESET_USER_INFO = 'reset_user_info' // 重置用户信息
export const RECEIVE_GOODS = 'receive_goods' // 接收商品数组
export const RECEIVE_RATINGS = 'receive_ratings' // 接收商家评价数组
export const RECEIVE_INFO = 'receive_info' // 接收商家信息
export const INCREMENT_FOOD_COUNT = 'increment_food_count' // 增加food中的count
export const DECREMENT_FOOD_COUNT = 'decrement_food_count' // 减少food中的count
export const CLEAR_CART = 'clear_cart' // 清空购物车
export const RECEIVE_SEARCH_SHOPS = 'receive_search_shops' // 接收搜索的商家数组
4. mutations.js改变状态函数
说明:此处动作为commit
中括号为计算属性名es6语法:作用是动态的写出当前函数名,方便后期维护:
[RECEIVE_ADDRESS](state,{address}){state.address=address}
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E5%90%8D
import {
RECEIVE_ADDRESS,
RECEIVE_CATEGORYS,
RECEIVE_SHOPS,
RECEIVE_USER_INFO,
RESET_USER_INFO,
RECEIVE_INFO,
RECEIVE_RATINGS,
RECEIVE_GOODS,
INCREMENT_FOOD_COUNT,
DECREMENT_FOOD_COUNT,
CLEAR_CART,
RECEIVE_SEARCH_SHOPS
} from './mutation-types.js'
export default{
//中括号为计算属性名es6语法:作用是动态的写出当前函数名,方便后期维护
[RECEIVE_ADDRESS](state,{address}){state.address=address},
[RECEIVE_CATEGORYS](state,{categorys}){state.categorys=categorys},
[RECEIVE_SHOPS](state,{shops}){state.shops=shops}
}
5.actions.js 提交改变状态
关键代码:提交mutation语法
commit(对象名,对象值)
commit(RECEIVE_ADDRESS,{address})
// 控制mutations
import {
RECEIVE_ADDRESS,
RECEIVE_CATEGORYS,
RECEIVE_SHOPS,
} from './mutation-types.js'
import {
reqAddress,
reqFoodCategorys,
reqShops,
} from '../api/index.js'
export default{
//获取地址用于改变状态
/* ★★★ ({commit,state})是解构写法相当于:
原(context.commit,context.state)*/
async getAddress({commit,state}){
//构造纬,经度参数用于请求
const geohash=state.latitude+','+state.longitude
const result=await reqAddress(geohash)
// 提交一个状态改变
if(result.code===0){
const address=result.data
//提交mutation,commit(对象名,对象值)
commit(RECEIVE_ADDRESS,{address})
}
},
//获取食物分类(首页滑动导航)
async getCategorys({commit}){
const result=await reqFoodCategorys()
if(result.code===0){
const categorys=result.data
commit(RECEIVE_CATEGORYS,{categorys})
}
},
// 获取商铺列表
async getShops({commit,state}){
/*const latitude=state.latitude
const longitude=state.longitude*/
const {longitude,latitude}=state
// 和api里的参数位置要相同经,纬度
result=await reqShops(longitude,latitude)
if(result.code===0){
const shops=result.data
commit(RECEIVE_SHOPS,{shops})
}
}
}
6.main.js挂载vuex
// 入口文件
import Vue from 'vue'
import App from './App'
import router from './router/index.js'
import store from './store' //【1】引入vuex管理状态
// es6写法
new Vue({
el: '#app',
render: h => h(App),
router,
store //【2】挂载vuex的store
})
7.app.vue试action(dispatch)一次
<script>
...略过
export default {
//挂载底部组件
components:{
FooterGuide
},
//【1】试dispatch一次
mounted(){
// getAddress来源actions.js
this.$store.dispatch('getAddress')
}
}
</script>
7.2(二选一项)actions的dispatch也可用如下方法调用,实现调用时直接用this.getAddress()
<script>
// 2.引入底部组件
import FooterGuide from './components/FooterGuide/FooterGuide.vue'
import {mapActions} from 'vuex' //【1】调用actions方法2:引入辅助函数
export default {
//3.挂载底部组件
components:{
FooterGuide
},
mounted(){
/* 调用actions方法1:getAddress来源actions.js
this.$store.dispatch('getAddress')*/
// 【3】调用actions方法2:
this.getAddress()
},
methods:{
// 【2】调用actions方法2:把getAddress方法解构到当前位置(把getAddress()函数直接复制进来)
// mapActions() 返回的是一个对象,
// 用了 ... 扩展符后,才可以放进一个对象里,
// 和其他组件内定义的method在同一个methods对象。
// 参数来自:store/actions.js内的方法
...mapActions(['getAddress'])
}
}
</script>
1-7步效果:
- 运行mongodb,再运行server端
- 运行client端
- 查看控制台网络请求、vue开发插件
6. getters.js 获取状态
三、vuex状态的使用
1.读取vuex的标题状态page/Msite.vue
此步要配合二.7步,先dispatch状态
====template内容:====
<!-- 【3】读取vuex管理的状态:地址的name,别忘记加冒号动态绑定 -->
<TopHeader :title='address.name'>
===script内容:====
import {mapState} from 'vuex' //【1】调用vuex管理的状态
computed:{
//【2】把vuex管理的地址状态对象复制到当前位置(来自store/state.js)
...mapState(['address'])
}
效果:http://localhost:8080/#/msite
2.用vue的action-dispatch进行ajax请求获取滑动导航数据,并放在vuex中管理,取得categorys数组,转换成2维数组,最后显示在视图中
1)page/msite.vue script部分
data(){
return{
//【4】从文档中确定导航小图标,图片的基础地址
baseImgUrl:'https://fuss10.elemecdn.com'
}
},
mounted(){
// 【1】用vuex的action发起ajax请求,获取滑动导航分类放入vuex状态中
//getCategorys来自actions.js
this.$store.dispatch('getCategorys')
...其它代码略过
},
computed:{
//【2】读取vuex管理的状态:地址/滑动导航分类解构到当前位置
/*mapState返回的值:函数(对象)
参数来自store/state.js*/
...mapState(['address','categorys'])
//【3】把categorys转化为二维数组x(滑动导航页数)*8(每页8个导航)
categoryArr(){
const max=8
const {categorys}=this
const arr=[]
let minArr=[]
categorys.forEach((c,index)=>{
if(minArr.length===0){
arr.push(minArr)
}
minArr.push(c)
if(minArr.length===max){
minArr=[]
}
})
return arr
}
},
至此应该能从vue调试工具中看到msite的相关数据
2) template部分:
可根据调试工具直接把导航改成v-for
<!--首页导航-->
<nav class="msite_nav">
<!-- 【1】如果导航内有数据则显示导航部分 -->
<div class="swiper-container" v-if='categorys.length'>
<div class="swiper-wrapper">
<!-- 【3】循环2维数据categoryArr读取所有的值并写入其中 -->
<div class="swiper-slide" v-for="(categorys,k) in categoryArr" :key='k'>
<a href="javascript:" class="link_to_food" v-for="(category,k) in categorys" :key='k'>
<div class="food_container">
<img :src="baseImgUrl+category.image_url">
</div>
<span>{{category.title}}</span>
</a>
</div>
</div>
<!-- Add Pagination -->
<div class="swiper-pagination"></div>
</div>
<!-- 【2】如果导航内没有数据,则显示一张替代图片 -->
<img src="./images/msite_back.svg" alt="back" v-else>
</nav>
效果:
如果categorys为空是否显示else视图:用调试工具把categorys数组在vuex内清空,保存
显示:替换的图片,注意阴影圆和小广场即是替代没有数据时的图片
3.swiper无法滑动问题解决watch,vm.nextTick()使用
当做到第二步后查看页面,发现滑动导航无法翻页
原因是swiper加载的时机不对
解决:使用watch监视指定数据,且在此数据的视图更新后再运行即可,原mounted刻删除
msite.vue/script里
export default{
watch:{//监视页面指定数据更新后执行
categorys(value){//categorys数据更新后
this.$nextTick(()=>{//categorys的数据的视图更新后再执行以下内容
//创建一个Swiper对象,实现滑动轮播
new Swiper('.swiper-container',{
//配置参数
direction: 'horizontal', //水平切换.垂直:vertical
loop: true, // 循环滚动
// 分页器
pagination: {
el: '.swiper-pagination',
clickable:true,
},
})
})
}
}
}
至此即可正常切换