话不多说,开干。
首先,使用vue-cli3创建一个项目
选择最后一个
选择自己需要的功能,安装之
最终项目的目录如下:
为了使讲解更简单明了,我们修改一下目录下的一些文件,将app.vue下的文件都注释掉,如下图:
现在我们开始一个最简单的vuex应用。打开store.js文件,编辑如下 :
打开App.vue文件,编辑如下:
main.js文件不需要做修改,然后启动项目:
最终显示结果如下:
是的,这就是最简单的一个vuex应用了。
State
如果世界上任何事情都是如此简单,那这个世界就太美好了,可是现实是残酷的。当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。所以就有了mapState这个辅助函数,它可以生成计算属性,让你偷个懒。修改store.js文件如下:
然后修改App.vue文件如下:
但是我还是想再偷偷懒,修改App.vue文件如下 :
怎么样,又少写了很多吧,但是呢我实在懒的不行,于是再修改App.vue如下 :
到这儿,小伙伴们应该想着够简单的了,不会再有比这更简单的了,呵呵,对!你又猜到了,还有更好的写法。另外,我也不喜欢computed对象后面跟着一个函数运算,感觉好怪,我更喜欢后面直接跟着一个花括号{},保持原有的展示风格,心里很舒爽。
mapState函数返回的是一个对象。如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed
属性,自从有了对象展开运算符,可以极大的简化写法,修改App.vue文件如下:
好了,到了这里,关于state应该是现有最终极的用法了。但是,仅仅拥有state是不够的,有些时候需要从state中派生出一些状态,对数据进行过滤并计算。这个时候,getter出现了。
Getter
Vuex允许在store中定义'getter'(可以认为是store的计算属性),这段红字一定要记住方便我们理解。
Getter接受state作为第一个参数:修改store.js文件如下:
修改App.vue文件如下:
最终页面展示如下:
Getter也可以接受其他的getter作为第二个参数,修改store.js如下:
修改App.vue文件如下:
页面展示:
可以通过让getter返回一个函数,来实现给getter参数。修改store.js如下:
修改App.vue如下:
展示如下:
getter当然也有它的辅助函数 mapGetters,如果对mapState辅助函数已经了解的话,这里应该不是问题。
Mutation
只有state是不够的,我们还需要操作它,state才会有意义。在vuex中更改store状态的唯一方法就是提交mutation。我们不能直接调用mutation,它接受state作为第一个参数。修改store.js文件如下 :
修改App.vue文件如下:
点击【add age】按钮,年龄(age)就会每次加1的方式往上累加。
但是可不是每次都是以1的量往上加,或者是其他不同的类型或数据,所以,mutation的第二个参数就是额外需要传入的参数(官网上叫载荷payload,感觉实在是没那么白话),修改store.js如下:
修改App.vue文件如下:
现在,年龄(age)就以每次10的量往上累加了。一般情况下,参数(载荷payload)以一个对象的方式传入。我们就来试试,以对象风格的方式提交(包含type属性的对象),修改 store.js 和 App.vue文件如下:
官网上说,可以使用常量的方式替代Mutatioin事件类型,用不用常量取决于使用者,我好像还没习惯这么用,暂时略过吧。值得注意的是(也是非常重要的):Mutation必须是同步函数。
好了,到了这儿小伙伴们一定会问,这个mutation是不是和state,getter一样,也有辅助函数呢?对极了,mapMutations辅助函数可以将组件中的methods映射为store.commit调用,使用方法也和mapState、mapGetters差不多,偷赖我们从没放弃过!修改store.js 和 App.vue文件如下:
有些时候,我们会希望在事件调用上的名称和mutation中的函数名称有些区别,那么给它个别称吧,如下:
关于Mutation基本到这儿就结束了,小伙伴还记得前面标红的字吗?mutation必须是同步的,那么如果我想异步操作呢?所以为了处理异步操作,Action登场。
Action
Action类似于mutation,不同是的:
1、Action 提交的是 mutation,不直接变更状态
2、Action可以包含任意异步操作
继续我们上面的例子,修改store.js文件如下:
修改App.vue文件如下:
我们可以看到,改变的地方并不多。
Action函数接受一个context对象参数,它与store实例具有相同方法和属性,但是context绝对不是store实例本身。
为了让代码更优雅,会经常使用 ES6 的参数解构,修改store.js如下:
是不是精简了许多,可读性更强了。
前面说过,Action和Mutation类似,所以Action同样支持载荷和对象方式分发,修改如下:
以载荷形式分发:
以对象形式分发:
现在,是时候来个异步了,瞧瞧Action真正的魅力,修改如下:
同样的,Action也有辅助函数mapActions,将组件的methods映射为store.dispatch调用。并且给个别名,如下:
最后,store.dispatch是可以处理Promise的,并且store.dispatch仍旧会返回Promise。如下:
Module
为了适应大型项目,状态集中到一个对象中会变得非常复杂,难以管理,所以store可以分割成模块(module),每个模块有自己的 state、mutation、action、getter。
在上例中我们作一些修改,把name和age分别分割成两个模块name和age,并把相应的 state、mutation、action、getter写进每个局部模块里面,注意局部与全局写法的不同区别。如下:
同时,删除原来export default {}中的内容,添加modules属性,并将两个模块加载进去,如下:
修改App.vue文件,在这个文件中其实修改的也不多,只是添加了各自的命名空间,如下 :
到了这儿,是不是应该已经大功告成了呢,显示还没有,关于Modules的更多应用,这里不再讲解,请稳步官网。
目录结构
而接下来我们要讨论的就是目录结构了,虽然我们将store.js已经划分了模块,但是它还是在一个文件当中,如果应用很庞大,模块数量很多,都写在store.js中明显是不合适的,也不方便管理,那么应该如何构建目录,如下:
先创建一个文件夹,命名为 stores,在stores文件夹里创建一个Index.js,用于存放基础框架布局,如下:
然后创建两个模块的文件夹 moduleName 和 moduleAge ,在每个文件夹里分别创建4个JS文件,index.js、getters.js、mutations.js、actions.js
moduleName/index.js
smoduleName/getters.js
smoduleName/mutations.js
smoduleName/actions.js
smoduleAge/index.js
smoduleName/getters.js
smoduleName/mutations.js
smoduleName/actions.js
以后就可以按照这个目录结构 及 构建方式 创建项目了。好了,大功告成!