文章目录

  • 1.前言
  • 2.Vuex如何使用
  • 3.State
  • 4.Mutation
  • 5.Getter
  • 6.Vue.js devtools
  • 7.Action
  • 8.Module

1.前言

上篇文章介绍了vuex的核心

2.Vuex如何使用

1. 安装vuex模块

npm install vuex --save

2. 作为插件使用

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

3. 定义容器

let store = new Vuex.Store()

4. 注入根实例

import store from "./store"
new Vue({el: '#app',store,components: { App },template: '<App/>'
})

3.State

store类似容器,包含应用的大部分状态(相当于组件的data数据),store的有几个特点如下
1. 一个页面只能有一个store
2. 状态存储是响应式的
3. 不能直接改变 store 中的状态,唯一途径显式地提交mutations

下面先来创建一个store容器,定义一个state对象,创建一个状态num

import vue from "vue"
import vuex from "vuex"vue.use(vuex);let stroe = new vuex.Store({state: {num: 100}
});export default stroe;

因为stroe 被注入了根实例,所以任何的组件都可以通过this.$store.state.num获取到这个num状态,还是用上篇文章的例子把App,Parent,Child,GrandSon组件都应用上这个状态,我这里只列出GrandSon的组件的代码,其他都一样!

<template><div><h2>GrandSon组件的number值:{{ num }}</h2><button>点击我改变number值</button><button>点击我改变number值</button></div>
</template><script>
export default {name: "GrandSon",computed: {num() {return this.$store.state.num;},}
};
</script>

每个组件都可以获取到这个状态,看下效果:

因为这个只有一个状态,当有多个状态(num,num1,num2)的话,每次都要将这些状态都声明为计算属性并且通过this.$store.state.xxx会有些重复和麻烦,所以就有个mapState 辅助函数能帮助简化写法,这里有多种写法!

1.第一种函数(箭头函数或者普通函数)写法

<template><div><h2>GrandSon组件的number值:{{ num }}</h2><button>点击我改变number值</button><button>点击我改变number值</button></div>
</template><script>
export default {name: "GrandSon",computed:mapState({//箭头函数写法//num: state => state.num,// num1:state => state.num1,// num2:state => state.num2,//普通函数写法num(state){return state.num},num1(state){return state.num1},num2(state){return state.num2},}),
};
</script>

2.第二种字符串写法

<template><div><h2>GrandSon组件的number值:{{ num }}</h2><button>点击我改变number值</button><button>点击我改变number值</button></div>
</template><script>
export default {name: "GrandSon",computed:mapState({num:"num",num1:"num1",num2:"num2"}),
};
</script>

3.第三种数组写法(如果计算属性名称和状态名称相同)

<template><div><h2>GrandSon组件的number值:{{ num }}</h2><button>点击我改变number值</button><button>点击我改变number值</button></div>
</template><script>
export default {name: "GrandSon",computed:mapState(["num","num1","num2"])
};
</script>

如果有其他的计算属性(局部计算属性)的话,应该使用es6的对象展开运算符将多个对象合并为一个

<template><div><h2>GrandSon组件的number值:{{ num }}</h2><button>点击我改变number值</button><button>点击我改变number值</button></div>
</template><script>
export default {name: "GrandSon",computed:{//组件局部计算属性testtest(){},...mapState(["num","num1","num2"])}
};
</script>

4.Mutation

mutation是唯一修改状态的事件回调函数,每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。注意:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。下面来写下写两个数字加减的mutation回调函数,回调函数可以接受两个参数,第一个参数是state状态,第二个参数是 mutation 的 载荷就是外面store.commit 传入额外的参数

//store index.js
import vue from "vue"
import vuex from "vuex"vue.use(vuex);let stroe = new vuex.Store({state: {num: 100},mutations: {addNumber(state, payload) {state.num += payload.n;},substractNumber(state, payload) {state.num -= payload.n;}
}
});export default stroe;
//GrandSon.vue
<template><div><h2>GrandSon组件的number值:{{ num }}</h2><button @click="addNumber">点击我改变number值</button><button @click="substractNumber">点击我改变number值</button></div>
</template><script>
export default {name: "GrandSon",computed:mapState(["num"]),methods: {addNumber() {//两种写法//this.$store.commit("addNumber", {n: 5});this.$store.commit({type:"addNumber",n:5});},substractNumber() {//两种写法//this.$store.commit("substractNumber", {n: 10});this.$store.commit({type:"substractNumber",n:10});}
};
</script>

只要触发点击事件,就会提交mutation(修改改状态的事件回调函数),状态发了改变后,那么只要用到这个状态的组件都自己会响应式发生变化

5.Getter

你可以理解它是一个状态计算属性,好比组件里面的computed属性,所以getter 的返回值也会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算,说白了就是对store 中的 state 做了一层封装,还是拿上面的例子来改写,GrandSon组件里面加法运算当大于等于120就等于120(不能再加上去了),我们来封装一下getter ,getter 可以接受两个参数,第一个是状态state,第二个参数是其他getter !

// store index.js
import vue from "vue"
import vuex from "vuex"vue.use(vuex);let stroe = new vuex.Store({state: {num: 100},getters: {filterNum:(state,getter) =>{return state.num > 120 ? 120 : state.num}},mutations: {addNumber(state, payload) {state.num += payload.n;},substractNumber(state, payload) {state.num -= payload.n;}}
});export default stroe;
//GrandSon.vue
<template><div><h2>GrandSon组件的number值:{{ computedNum}}</h2><button @click="addNumber">点击我改变number值</button><button @click="substractNumber">点击我改变number值</button></div>
</template><script>
export default {name: "GrandSon",computed:{computedNum() {return this.$store.getters.filterNum;},...mapState(["num"]),}methods: {addNumber() {//两种写法//this.$store.commit("addNumber", {n: 5});this.$store.commit({type:"addNumber",n:5});},substractNumber() {//两种写法//this.$store.commit("substractNumber", {n: 10});this.$store.commit({type:"substractNumber",n:10});}
};
</script>

看下下面的效果:


当然如果有多个getter,可以使用mapGetters 辅助函数来简化写法:

1. 如果computed计算属性名和getters里面的属性名一样!

//GrandSon.vue
<template><div><h2>GrandSon组件的number值:{{ filterNum}}</h2><button @click="addNumber">点击我改变number值</button><button @click="substractNumber">点击我改变number值</button></div>
</template><script>
import { mapState, mapGetters } from "vuex";
export default {name: "GrandSon",computed:{...mapGetters(["filterNum"]),...mapState(["num"]),}methods: {addNumber() {//两种写法//this.$store.commit("addNumber", {n: 5});this.$store.commit({type:"addNumber",n:5});},substractNumber() {//两种写法//this.$store.commit("substractNumber", {n: 10});this.$store.commit({type:"substractNumber",n:10});}
};
</script>

2. computed计算属性名和getters里面的属性名不一样!

//GrandSon.vue
<template><div><h2>GrandSon组件的number值:{{ computedNum}}</h2><button @click="addNumber">点击我改变number值</button><button @click="substractNumber">点击我改变number值</button></div>
</template><script>
import { mapState, mapGetters } from "vuex";
export default {name: "GrandSon",computed:{...mapGetters({computedNum: "filterNum"}),...mapState(["num"]),}methods: {addNumber() {//两种写法//this.$store.commit("addNumber", {n: 5});this.$store.commit({type:"addNumber",n:5});},substractNumber() {//两种写法//this.$store.commit("substractNumber", {n: 10});this.$store.commit({type:"substractNumber",n:10});}
};
</script>

6.Vue.js devtools

这是goolgle的一个调试vuex的插件,可以很清楚的看到组件之间的层级关系,还有各个组件的局部数据

还可以很清楚的看到各个状态的变化,还可以通过时间旅行回到过去的任意状态!并且可以看到这些状态都是可预测的,逻辑非常的清晰,调试起来非常的方便!

7.Action

Action 和Mutation非常的相似,只不过Mutation是同步的,Action是异步的,如果你提交一个异步的Mutation就不可以预测了,我们可以通过上面的Vue.js devtools来做个例子!把mutations的addNumber函数该成延迟5秒再执行

上图可以看到我迅速的点击了3次,通过插件时间旅行发现状态都是100,这样就有问题的,这种异步情况就是不可预测的,那么来改下,把异步操作放到Action里面

// store index.js
import vue from "vue"
import vuex from "vuex"vue.use(vuex);let stroe = new vuex.Store({state: {num: 100},getters: {filterNum:(state,getter) =>{return state.num > 120 ? 120 : state.num}},mutations: {addNumber(state, payload) {state.num += payload.n;},substractNumber(state, payload) {state.num -= payload.n;}},actions: {addAction(context){console.log(context);setTimeout(()=>{context.commit("addNumber");},5000)}
});export default stroe;

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,看到里面有commit,所以Action 和Mutation区别就是Action提交的是Mutation,而不是直接变更状态,并且Action 可以包含任意异步操作而Mutation只能是同步的

那么组件也需要分发 Action用到store.dispatch 方法触发,来修改下GrandSon组件如下:

//GrandSon.vue
<template><div><h2>GrandSon组件的number值:{{ computedNum}}</h2><button @click="addNumber">点击我改变number值</button><button @click="substractNumber">点击我改变number值</button></div>
</template><script>
import { mapState, mapGetters } from "vuex";
export default {name: "GrandSon",computed:{...mapGetters({computedNum: "filterNum"}),...mapState(["num"]),}methods: {addNumber() {this.$store.dispatch("addAction");},substractNumber() {//两种写法//this.$store.commit("substractNumber", {n: 10});this.$store.commit({type:"substractNumber",n:10});}
};
</script>

既然context对象里面有dispatch属性,那么如果有多个异步函数(a,b,c),先执行异步a函数,在异步b函数里面可以dispatch a函数,异步c函数可以dispatch 函数,dispatch 函数也可以和commit一样接受一个type属性和参数的对象,下面来模拟下:

// store index.js
import vue from "vue"
import vuex from "vuex"vue.use(vuex);let stroe = new vuex.Store({state: {num: 100},getters: {filterNum:(state,getter) =>{return state.num > 120 ? 120 : state.num}},mutations: {addNumber(state, payload) {state.num += payload.n;},substractNumber(state, payload) {state.num -= payload.n;}},actions: {addActionA(context, payload) {setTimeout(() => {context.commit({type: "addNumber",n:payload.n});}, 1000)},addActionB(context, payload) {setTimeout(() => {context.dispatch({type: "addActionA",n:payload.n});}, 1000)},addActionC(context, payload) {setTimeout(() => {context.dispatch({type: "addActionB",n:payload.n});}, 1000)}
});export default stroe;

GrandSon组件需要修改下分下事件的名称addActionC

//GrandSon.vue
<template><div><h2>GrandSon组件的number值:{{ computedNum}}</h2><button @click="addNumber">点击我改变number值</button><button @click="substractNumber">点击我改变number值</button></div>
</template><script>
import { mapState, mapGetters } from "vuex";
export default {name: "GrandSon",computed:{...mapGetters({computedNum: "filterNum"}),...mapState(["num"]),}methods: {addNumber() {this.$store.dispatch({type:"addActionC",n:5});},substractNumber() {//两种写法//this.$store.commit("substractNumber", {n: 10});this.$store.commit({type:"substractNumber",n:10});}
};
</script>

点击加法事件,3秒之后会提交mutations改变状态,看下效果:

context既然是对象,你也可以利用es6的结构赋值来简写

  actions: {addActionA({commit}, payload) {setTimeout(() => {commit({type: "addNumber",n:payload.n});}, 1000)},addActionB({dispatch}, payload) {setTimeout(() => {dispatch({type: "addActionA",n:payload.n});}, 1000)},addActionC({dispatch}, payload) {setTimeout(() => {dispatch({type: "addActionB",n:payload.n});}, 1000)}}

如果你觉得写this.$store.dispatch麻烦,也可以用mapActions来简写,下面接着来修改下GrandSon组件!

//GrandSon.vue
<template><div><h2>GrandSon组件的number值:{{ computedNum}}</h2><button @click="addNumber({n:5})">点击我改变number值</button><button @click="substractNumber">点击我改变number值</button></div>
</template><script>
import { mapState, mapGetters } from "vuex";
export default {name: "GrandSon",computed:{...mapGetters({computedNum: "filterNum"}),...mapState(["num"]),}methods: {//如果事件方法名和分发事件名相同,可以这么写//...mapActions(["addActionC"]),...mapActions({//这里传的参数{n:5}应该写成上面的addNumber函数参数//@click="addNumber({n:5})"addNumber: "addActionC",}),substractNumber() {//两种写法//this.$store.commit("substractNumber", {n: 10});this.$store.commit({type:"substractNumber",n:10});}
};
</script>

8.Module

Module可以将 store 分割成不同的模块,上面的例子演示的只有一个模块,可以把它分离出来 ,看下面代码:

import vue from "vue"
import vuex from "vuex"vue.use(vuex);let moduleA={state: {num: 100,num1: 200,num2: 300},getters: {filterNum: (state) => {return state.num > 120 ? 120 : state.num}},mutations: {addNumber(state, payload) {state.num += payload.n;},substractNumber(state, payload) {state.num -= payload.n;}},actions: {addActionA({commit}, payload) {setTimeout(() => {commit({type: "addNumber",n:payload.n});}, 1000)},addActionB({dispatch}, payload) {setTimeout(() => {dispatch({type: "addActionA",n:payload.n});}, 1000)},addActionC({dispatch}, payload) {setTimeout(() => {dispatch({type: "addActionB",n:payload.n});}, 1000)}}
}let stroe = new vuex.Store({modules:{//moduleA:moduleA  也是这样写,下面是es6的简写moduleA}
});export default stroe;

既然现在已经分了模块,那么组件要取到模块的状态,应该怎么取呢,先把this.$store打印出来看下:

  computed: {num(){console.log(this.$store);}}


打印出来之后,应该知道怎么写了

  computed: {num(){return  this.$store.state.moduleA.num;}}

Vue全家桶系列之Vuex(二)相关推荐

  1. Vue全家桶系列之Vuex(一)

    文章目录 1.前言 2.Vuex是什么? 3.什么是集中式状态管理模式? 4.什么情况下使用Vuex?它能帮我们解决什么问题? 5.简单的例子 1.前言 Vuex是一个很棒的状态管理库,它很简单并且非 ...

  2. Vue全家桶系列之Vuex(三)

    文章目录 1.前言 2.模块的命名空间 3.模块的动态注册 4.vuex严格模式 5.vuex插件 5.1 subscribe 5.2 subscribeAction 5.3 replaceState ...

  3. Vue全家桶系列之Vue-router(五)

    文章目录 1.前言 2.路由过渡效果 3.编程式的导航 4.导航守卫 4.1 全局守卫 4.2 路由独享的守卫 4.3组件内的守卫 4.4导航解析流程 1.前言 上篇文章说了vue-router的路由 ...

  4. vue全家桶系列之网易云音乐(移动版)

    一个精致的网易云音乐webapp 网易云音乐(移动版) api来源(感谢Binaryify不断更新的网易云音乐接口,这也将是这个项目不断拓展下去的坚实依托) 源码地址 项目预览(web端在chrome ...

  5. Vue 全家桶 + Electron 开发的一个跨三端的应用

    GitHub Repo:vue-objccn Follow: halfrost · GitHub 项目地址:https://github.com/halfrost/vue-objccn 利用 Vue. ...

  6. Vue全家桶 + webpack 构建单页应用初体验

    文章指南 主题   承接这上一篇Vue + Webpack 构建模块化开发框架详解,我们知道了如何使用webpack对vue进行打包,从而开始我们的前端模块化开发之路,这一篇在上一篇的基础上讲解 Vu ...

  7. Vue 全家桶 + Electron 开发的一个跨三端的应用 1

    代码地址如下: http://www.demodashi.com/demo/11738.html GitHub Repo:vue-objccn Follow: halfrost · GitHub 利用 ...

  8. Vue开发入门(二) | 说说Vue全家桶有哪些~

    全家桶,顾名思义,就是一个系列,可以组合开发成完整强大的Vue项目 前言: *Vue两大核心思想:组件化和数据驱动. 组件化:把整体拆分为各个可以复用的个体 数据驱动:通过数据变化直接影响bom展示, ...

  9. vue取div当前宽度_【前端大神面考面试官系列】入门Vue全家桶

    (给达达前端加星标,提升前端技能) 面试官问:Vue如何安装的呢? 达达回答:Vue的安装可以从两个方面进行安装,第一种是CDN引入,第二种是NPM安装. CDN引入 <script src=& ...

最新文章

  1. CMD批量创建目录-配置管理
  2. 地图数据快速渲染------基于传统GIS平台多服务器切片
  3. 分布式ID业界解决方案
  4. 【bzoj2693】jzptab 莫比乌斯反演+线性筛
  5. mongdb 建立了索引唯一性还能重复插入?_「数据库系列」Postgres性能调优——Index...
  6. 携程elong相继牵手支付宝转“危”为“机”
  7. 全国计算机一级知识题及答案解析,全国计算机等级考试一级试题库大全完整版附参考答案...
  8. html-文本框和单选框
  9. 王者荣耀不同服务器能显示微信好友吗,王者荣耀怎么看微信好友在那个区
  10. Java的自动拆箱和装箱是Java语言的一颗语法糖
  11. 交流电机Clark变换中的功率不变约束与幅值不变约束
  12. 网络看不到计算机和设备,网络发现已关闭,网络计算机和设备不可见。请启用网络和共享中心中的网络发现。解决方法...
  13. 潮汕地区2-汕头观感
  14. vue 之手机号验证、正则验证手机号是否正确、手机号验证码信息弹窗
  15. 美团点评智能支付核心交易系统的可用性实践
  16. 成都新房二手房房价采集
  17. 2019,入职互联网公司的好时机,你抓住了没?
  18. ffmpeg插帧算法
  19. 【面试题目】你有12个硬币,其中有一个的重量与其他的不一样,有三次使用测量平衡的机会来找出重量不同的那个。该怎么做呢?
  20. 湘湘学习之旅 越努力 越幸运

热门文章

  1. 计算机软件著作权侵权行为的认定,计算机软件著作权侵权行为认定方法探讨.doc...
  2. 制造业车间生产管理系统(精诚MES)
  3. 2020-10-13 WPS VBA 添加控件,显示“库没有注册”
  4. 02笔记 离散数学——命题逻辑——基于离散数学(第3版)_章炯民,陶增乐
  5. java语言输出当前日期时间的方法
  6. NVM Node版本管理工具
  7. Android回归Linux核心怀抱
  8. APP开发项目流程详解,长知识了!
  9. 中国信通院X容联云|联合发布《客服中心智能化技术和应用研究报告》
  10. NKOJ-Unknow 不死的 LYM