Vuex 调试与使用
(一) Vuex基础官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。Vuex就是提供一个在多个组件间共享状态的插件[1]. 安装Vuex插件npm install vuex --save[2]. 应用Vuex插件在/src 目录下创建store目录,并且在store目录下新建index.js文件import Vue from "vue";import Vuex from
如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式 就足够您所需了。
但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
(一) 简单状态管理
https://cn.vuejs.org/v2/guide/state-management.html#%E7%AE%80%E5%8D%95%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86%E8%B5%B7%E6%AD%A5%E4%BD%BF%E7%94%A8
接着我们继续延伸约定,组件不允许直接变更属于 store 实例的 state,而应执行 action 来分发 (dispatch) 事件通知 store 去改变,我们最终达成了 Flux 架构。这样约定的好处是,我们能够记录所有 store 中发生的 state 变更,同时实现能做到记录变更、保存状态快照、历史回滚/时光旅行的先进的调试工具。
<div id="app">
<cpn></cpn>
<cpn2></cpn2>
</div>
<template id="cpn">
<div>
<button @click="setMessage">cpn设置msg</button>
<p>{{sourceOfTruth}}</p>
<p>{{sharedState}}</p>
</div>
</template>
<template id="cpn2">
<div>
<button @click="setMessage">cpn2设置msg</button>
<p>{{sourceOfTruth}}</p>
<p>{{sharedState}}</p>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const store = {
debug: true,
state: {
message: 'hello'
},
setMessageAction(newValue) {
if (this.debug) console.log('setMessageAction triggered with', newValue)
this.state.message = newValue
},
clearMessageAction() {
if (this.debug) console.log('clearMessageAction triggered')
this.state.message = ''
}
}
const cpn = {
template: "#cpn",
data() {
return {
sourceOfTruth: 'cpn',
sharedState: store.state
}
},
methods: {
setMessage(){
store.setMessageAction('你好');
}
}
}
const cpn2 = {
template: "#cpn2",
data() {
return {
sourceOfTruth: 'cpn2',
sharedState: store.state
}
},
methods: {
setMessage(){
store.setMessageAction('hi');
}
}
}
const app = new Vue({
el: "#app",
components: {
cpn,
cpn2
}
});
</script>
(二) Vuex基础
-
官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
-
什么时候使用Vuex
多个组件依赖于同一状态;
来自不同组件的行为需要变更同一状态; -
https://vuex.vuejs.org/zh/
[1]. 安装Vuex插件
npm install vuex --save
[2]. 应用Vuex插件
-
在/src 目录下创建store目录,并且在store目录下新建index.js文件
//引入Vue核心库 import Vue from "vue"; //引入Vuex import Vuex from "vuex"; Vue.use(Vuex); //准备actions--用于响应组件中的动作 const actions = {}; //准备mutations--用于操作数据(state) const mutations = {}; //准备state--用于存储数据 const state = {}; const store = new Vuex.Store({ actions, mutations, state }); export default store; //导出store
-
在main.js文件中
//... import store from "./store"; //引入store //.... new Vue({ el: '#app', // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件 store,//可以在其他页面使用$store调用 render: h => h(App) };
-
在App.vue文件中
<template> <div id="app"> <hello-world></hello-world> </div> </template> <script> import HelloWorld from "@/components/HelloWorld"; export default { name: 'App', components: { HelloWorld, } } </script>
-
在HelloWorld.vue文件中使用
<template> <div> <p>这是HelloWorld文件</p> <h2>{{$store.state.score}}</h2> </div> </template> <script> export default { name: 'HelloWorld', } </script>
(三) 安装devtools(浏览器插件)
-
devtools是浏览器插件需要
进入谷歌网上应用店:https://chrome.google.com/webstore
搜索:devtools -
重启浏览器
(四) Vuex核心概念
[1]. Actions 异步操作
-
Actions更倾向于整合多个mutation操作
-
若没有网络请求或其他业务逻辑,组件中可以越过actions,即不写dispatch,直接调用mutations中的方法。
-
actions属性中的方法有一个默认的参数:context,context是和store对象具有相同方法和属性的对象,但是注意, 这里它们并不是同一个对象。所以可以使用contenxt.commit调用一个mutation中的方法或者通过context.state和context.getters来获取state的值和getters中的方法,当然也可以使用context.dispatch调用Actions中的方法。
-
在组件中使用this.$store.dispatch(‘actions中的方法’, ‘传递的参数’)来调用actions中的方法。
<template> <p>学生信息:{{$store.dispath('getStuInfo')}}</p> </template > <script> export default { methods: { asyncInfo(){ this.$store.dispath('getStuInfo') }, } } </script>
-
store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:
store.dispatch('actionA').then(() => { // ... })
-
代码
/src/store/index.js://引入Vue核心库 import Vue from "vue"; //引入Vuex import Vuex from "vuex"; Vue.use(Vuex); const store = new Vuex.Store({ state : { info :{ name: '测试响应式', age: 52 } }, getters: { }, mutations: { updateInfo(state, info){ state.info.name = info; } }, actions: { asyncInfo(context, info){ setTimeout(()=>{ context.commit('updateInfo', info); }, 1000); }, //actions 结合 promise asyncWithPromise(){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('异步成功'); }, 1000); }); } } });
在HelloWorld.vue中添加
<template> <div> <button @click="asyncInfo()">异步修改info</button> <button @click="asyncWithPromise()">异步结合promise</button> </div> </template> <script> export default { name: 'HelloWorld', methods: { asyncInfo(){ this.$store.dispatch('asyncInfo', '异步测试代码'); }, asyncWithPromise(){ this.$store.dispatch('asyncWithPromise', '异步测试代码').then((res)=>{console.log(res);}); } } } </script>
[2]. Mutations 状态更新
- ①. mutations中的方法是Vuex中唯一可以操作State的方式。
- ②. mutations 中的方法默认有一个参数state
- ③. 在组件中使用 commit 调用mutations 中的方法
<!-- 组件模板中调用 --> <p>{{$store.commit('mutations中的方法名称', '参数');}}</p> <!-- 组件得配置对象中直接调用mutations中的方法(允许这么操作)--> this.$store.commit('mutations中的方法名称', '参数');
- ④ mutation中的方法完成的事情尽可能单一
- ⑤传递参数
参数被称为是mutation的载荷(Payload)。多个参数时,使用对象进行传参。
在HelloWorld.vue中添加
/src/store/index.js:<template> <div> <button @click="addTen()">score +10</button> <button @click="addNum(100)">score +num</button> <button @click="addStu()">add stu</button> </div> </template> <script> export default { name: 'HelloWorld', methods: { addTen(){ //没有参数,组件直接调用mutation中的scoreAddTen方法 this.$store.commit('scoreAddTen'); }, addNum(num){ this.$store.commit('scoreAddNum', num); }, addStu(){ this.$store.commit('addStu', {name:'孙七', age: 22}); } } } </script>
在组件中使用另一种风格 commit 调用mutations 中的方法,Mutation中的处理方式是将整个commit的对象作为payload使用。const store = new Vuex.Store({ mutations: { //1.不传递参数 scoreAddTen(state){ state.score += 10; }, // 2.传递一个参数 scoreAddNum(state, num){ state.score += num; }, // 3.用对象传递多个参数时 addStu(state, info){ state.students.push(info); } } }); export default store;
this.$store.commit({ type: "mutations中的事件名称", "参数1": "参数值1", "参数2": "参数值2", });
- ⑥. Mutation同步函数和异步操作
- 通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.
主要的原因是当我们使用devtools时, devtools可以帮助我们捕捉mutation的快照.
但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成. - 通常情况下, 不要在mutation中进行异步的操作
- 通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.
[3]. State (单一状态树)
-
英文名称是Single Source of Truth (SSOT),也可以翻译成单一数据源。
-
所有状态要放在一个store,整个项目要只有一个store
-
Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新
这就要求我们必须遵守一些Vuex对应的规则:
1). 提前在store中初始化好所需的属性.2).当给state中的对象添加新属性时, 使用下面的方式:
- 方式一: 使用Vue.set(‘被添加属性的对象’, ‘新属性名’, ‘属性值’)
- 方式二: 用新对象给旧对象重新赋值3). 删除state中的对象属性
Vue.delete(“对删除的属性对象”, “被删除的属性名”);4). 代码
/src/store/index.js:.... const store = new Vuex.Store({ state : { ... info :{ name: '测试响应式', age: 52 } }, .... mutations: { .... // 给info添加属性 addAttr(state){ //下面代码可以添加message属性但是无法做到响应式 // state.info.message = 'hello'; Vue.set(state.info, 'message', 'hello'); } } });
在HelloWorld.vue中添加
<template> <div> <h2>{{$store.state.info}}</h2> <button @click="addAttr()">给info添加一个属性</button> </div> </template> <script> export default { methods: { .... addAttr(){ this.$store.commit('addAttr'); } } } </script>
[4]. getters 获取状态
-
getters相当于store的computed(计算属性),getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
-
getters中第一个的参数是state,这个参数就是store中的state属性,可以利用这个参数来获取store中state的属性值
-
getters中第二个参数是getters,这个参数getters本身,可以利用这个参数来获取getters中的其他方法。
-
getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数。
-
案例:
/src/store/index.js中定义了三个方法:import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); const store = new Vuex.Store({ state : { score: 123, students: [ {name: '张三', age: 18}, {name: '李四', age: 19}, {name: '王五', age: 20}, {name: '李刘', age: 21} ] }, getters: { //获取学生年龄大于19的人 greaterAge19(state){ return state.students.filter(stu => stu.age>19); }, //获取学生年龄大于19的个数 greaterAge19Length(state, getters){ return getters.greaterAge19.length; }, //获取学生年龄大于age的学生 gettersAge(state){ return function(age){ return state.students.filter(stu => stu.age>age); } } } }); export default store;
在HelloWorld.vue组件中
<template> <div> <p>这是HelloWorld文件</p> <h2>{{$store.state.score}}</h2> <h2>{{$store.getters.greaterAge19}}</h2> <h2>{{$store.getters.greaterAge19Length}}</h2> <h2>{{$store.getters.gettersAge(20)}}</h2> </div> </template> <script> export default { name: 'HelloWorld', } </script>
[6]组件绑定的辅助函数mapState,mapGetter,mapMutations等
- https://vuex.vuejs.org/zh/guide/state.html#mapstate-%E8%BE%85%E5%8A%A9%E5%87%BD%E6%95%B0
- https://vuex.vuejs.org/zh/api/#%E7%BB%84%E4%BB%B6%E7%BB%91%E5%AE%9A%E7%9A%84%E8%BE%85%E5%8A%A9%E5%87%BD%E6%95%B0
①mapActions
- mapActions方法:用于帮助我们生成与
actions
对话的方法,即:包含$store.dispatch(xxx)
的函数 - 若需传递参数需要在模板中绑定事件时就传递好参数,否则参数是事件对象。
<template> <div> <button type="button" @click="multiply">乘</button> <!-- 将num作为参数传入 --> <button type="button" @click="multiply1(num)">乘</button> <!-- 如果不传参数默认传入event--> <button type="button" @click="cheng">乘</button> </div> </template> <script> import { mapState, mapMutations, mapActions } from "Vuex"; export default { name: "App", data() { return { num: 1, }; }, methods: { multiply() { this.$store.dispatch("cheng", this.num);//调用actinos中的chengt方法并将num当做参数传递过去 }, //数组写法 ...mapMutations([cheng]),//等同于 cheng(value){ this.$store.dispatch('cheng', value)} //对象写法 ...mapMutations({ multiply1: "cheng" }),//等同于 multiply1(value){ this.$store.dispatch('cheng', value)} }, }; </script>
②mapMutations
- mapMutations 方法:用于帮助我们生成与
mutations
对话的方法,即:包含$store.commit(xxx)
的函数 - 若需传递参数需要在模板中绑定事件时就传递好参数,否则参数是事件对象。
<template> <div> <button type="button" @click="increment">加</button> <!-- 将n作为参数传入 --> <button type="button" @click="increment1(n)">加</button> <!-- 如果不传参数默认传入event--> <button type="button" @click="JIA">加</button> </div> </template> <script> import { mapMutations } from 'vuex' export default { data(){ return { n: 20 } }, methods: { increment(){ this.$store.commit('JIA', this.n);//调用mutations中的JIA方法并将n当做参数传递过去 }, //数组写法 ...mapMutations(['JIA'])//等同于 JIA(value){ this.$store.commit('JIA', value)} //对象写法 ...mapMutations({increment1: 'JIA'}),//等同于 increment1(value){ this.$store.commit('JIA', value)} } } </script>
③ mapState
- mapState方法:用于帮助我们映射
state
中的数据为计算属性<template> <div> <p>计算结果为:{{sum1}}</p> <p>计算结果为:{{sum2}}</p> <p>计算结果为:{{sum}}</p> </div> </template> <script> import {mapState} from 'Vuex'; export default { name: "App", computed:{ sum1(){ return this.$store.state.sum; //调用state中的sum }, //数组写法 ...mapState(['sum'])//等同于sum(){ return this.$store.state.sum} //对象写法 ...mapState({sum2: 'sum'}), //等同于sum2(){ return this.$store.state.sum} }, }; </script>
④mapGetters
-
mapGetters方法:用于帮助我们映射
getters
中的数据为计算属性<template> <div> <p>计算结果为:{{bigSum1}}</p> <p>计算结果为:{{bigSum}}</p> <p>计算结果为:{{bigSum2}}</p> </div> </template> <script> import { mapGetters } from 'vuex' export default { // ... computed: { bigSum1(){ return this.$store.getters.bigSum;//调用getters中的bigSum方法 }, //数组写法 ...mapGetters(['bigSum']),//等同于bigSum(){ return this.$store.getters.bigSum} //对象写法 ...mapGetters({bigSum2: 'bigSum'}),//等同于bigSum2(){ return this.$store.getters.bigSum} } } </script>
(五). modules
[1]语法与解释
https://vuex.vuejs.org/zh/guide/modules.html
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
-
将不同模块进行分割(/src/store/index.js)
import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); //用户模块 const userInfo = { namespaced: true, //开启命名空间 state: {}, actions: {}, mutations: {}, getters: {}; const goodsInfo = { namespaced: true, //开启命名空间 state: {}, actions: {}, mutations: {}, getters: {} }; export default new Vuex.Store({ modules: { userAbout: userInfo, //当前模块的命名空间: 模块数据 goodsAbout: goodsInfo } });
-
开启命名空间后,组件中读取state数据
//方式一:this.$store.state.命名空间.state属性名 this.$store.state.userAbout.userList //方式二:...mapState('命名空间', ['state中的属性名']) ...mapState('goodsAbout', ['goodsList', 'searchGoodsInfo']),
-
开启命名空间后,组件中调用getters中的方法:
//方式一:this.$store.getters['命名空间/getters中的方法名'] this.$store.getters['userAbout/userCount'] //方式二:...mapGetters('命名空间', ['getters中的方法名']) ...mapGetters('goodsAbout', ['goodsCount'])
-
开启命名空间后,组件中调用actions中的方法
//方式一:this.$store.dispatch('命名空间/方法名', 参数) this.$store.dispatch('personAbout/addPersonWang',person) //方式二:...mapActions('命名空间', {'当前组件希望使用的名字':'actions中的方法的名字'}) ...mapActions('goodsAbout', {addGoods: 'addGoods'}),
-
开启命名空间后,组件中调用mutations中的方法
//方式一://调用mutations中的方法:this.$store.commit('命名空间/方法名', 参数) this.$store.commit('userAbout/getUserInfoByName', this.userName) //方式二://调用Mutations的方法:...mapMutations('命名空间', {'当前组件希望使用的名字':'mutations中的方法的名字'}) ...mapMutations('goodsAbout', {getGoodsInfo: 'getGoodsInfoByName'})
[2]用例
-
vuex的存放位置:/src/store/index.js
import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); const userInfo = { namespaced: true, //开启命名空间 state: { userList: [], autoIncrementId: 0, searchUserInfo: null }, actions: { addUser(context, name) { var userInfo = { id: context.state.autoIncrementId, name: name } context.commit('ADD_USER', userInfo); context.state.autoIncrementId ++; } }, mutations: { ADD_USER(state, userInfo) { state.userList.push(userInfo); }, getUserInfoByName(state, name) { for (const user of state.userList) { if (user.name === name) { state.searchUserInfo = user; } } } }, getters: { userCount(state) { return state.userList.length } } }; const goodsInfo = { namespaced: true, //开启命名空间 state: { goodsList: [], autoIncrementId: 0, searchGoodsInfo: null }, actions: { addGoods(context, name) { var goodsInfo = { id: context.state.autoIncrementId, name: name } context.commit('ADD_GOODS', goodsInfo); context.state.autoIncrementId ++; } }, mutations: { ADD_GOODS(state, goodsInfo) { state.goodsList.push(goodsInfo); }, getGoodsInfoByName(state, name) { for (const goods of state.goodsList) { if (goods.name === name) { state.searchGoodsInfo = goods; } } } }, getters: { goodsCount(state) { return state.goodsList.length } } }; export default new Vuex.Store({ modules: { userAbout: userInfo, //命名空间: 数据 goodsAbout: goodsInfo } });
-
user组件 /src/components/User.vue
<template> <div> <input type="text" v-model="userName" /> <button @click="addUser">添加用户</button> <button @click="getUserInfo">查找用户</button> <p>用户总数:{{userCount}}</p> <p>用户列表</p> <ul> <li v-for="user in userList" :key="user.id">{{user.name}}</li> </ul> <p>查询到的商品信息:{{searchUserInfo}}</p> </div> </template> <script> export default { name: "User", data() { return { userName: "", }; }, computed:{ userList(){ //读取state数据:this.$store.state.命名空间.state属性名 return this.$store.state.userAbout.userList }, searchUserInfo(){ //读取state数据 return this.$store.state.userAbout.searchUserInfo }, userCount(){ //调用getters中的方法:this.$store.getters['命名空间/getters中的方法名'] return this.$store.getters['userAbout/userCount'] }, }, methods:{ addUser(){ //调用actions中的方法:this.$store.dispatch('命名空间/方法名', 参数) this.$store.dispatch('userAbout/addUser', this.userName); }, getUserInfo(){ //调用mutations中的方法:this.$store.commit('命名空间/方法名', 参数) this.$store.commit('userAbout/getUserInfoByName', this.userName) } } }; </script>
-
goods组件 /src/components/Goods.vue
<template> <div> <input v-model="goodsName"> <button @click="addGoods(goodsName)">添加商品</button> <button @click="getGoodsInfo(goodsName)">查询商品</button> <p>商品总数:{{goodsCount}}</p> <p>商品列表</p> <ul> <li v-for="goods in goodsList" :key="goods.id">{{goods.name}}</li> </ul> <p>查询到的商品信息:{{searchGoodsInfo}}</p> </div> </template> <script> import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'; export default { name:'Goods', data(){ return { goodsName: '' } }, computed:{ //读取State的数据:...mapState('命名空间', ['state中的属性名']) ...mapState('goodsAbout', ['goodsList', 'searchGoodsInfo']), //调用getters的方法:...mapGetters('命名空间', ['getters中的方法名']) ...mapGetters('goodsAbout', ['goodsCount']) }, methods:{ //调用Actions的方法:...mapActions('命名空间', {'当前组件希望使用的名字':'actions中的方法的名字'}) ...mapActions('goodsAbout', {addGoods: 'addGoods'}), //调用Mutations的方法:...mapMutations('命名空间', {'当前组件希望使用的名字':'mutations中的方法的名字'}) ...mapMutations('goodsAbout', {getGoodsInfo: 'getGoodsInfoByName'}) } } </script>
-
App组件 /src/App.vue
<template> <div> <Goods></Goods> <hr> <User></User> </div> </template> <script> import Goods from "./components/Goods.vue"; import User from "./components/User.vue"; export default { name: "App", components: { Goods, User }, }; </script>
更多推荐
所有评论(0)