vuex的使用及持久化state的方式
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
当我们接触vuex的时候,这是我们最先看到的一句官方引导。
从这句话中,我们可以得到如下几个信息:
1、vuex是一个为vue而存在的特化的Flux,如同数据库中的弱实体一样,离开了vue,vuex就用不了。反之可以看到redux就不存在,无论是vue还是react,redux都可以使用。所以这里体现的vuex的“特性”,redux则具备“普适性”
2、集中式的管理说明vue中所有的组件的状态都是存在于vuex中
3、使用vuex你就要遵循我的规则,这样组件中状态的变化我才能跟踪的到。
1.项目中vuex目录的搭建

上图是我在这篇文章中,vue整体项目骨架的局部。
vuex使用的是单一的状态树,我们的vue应用将仅仅包含一个 store 的实例。所以当我们将store挂载到vue的实例上以后,我们可以通过this.$store取到vuex里面的各个部分。
2.index.js
import vue from 'vue'
import vuex from 'vuex'
import mutations from './mutation'
import getters from './getter' vue.use(vuex) const state = {
isLoading:false
} export default new vuex.Store({
state,
getters,
mutations
})
在index这个文件中,我们会去定义我们需要在vuex中存储的状态初始值。
比如说,我在上面的state对象中去存储了一个isLoading属性,该属性我准备用它来标识我请求backend API的时候显示,在请求完成后消失的这样一个loading的效果,来缓解一下用户的等待心理。
3.Mutation(mutation.js)
一般来说,我们在项目中最常用的就是mutation.js里面的方法了。因为更改vuex中的store里的state的唯一的方式就是提交mutation。
在vuex中,每个mutation都有一个字符串的事件类型(mutation-type)和一个回调函数(handler)。
这个回调函数可接受两个参数,第一个参数为state,第二参数是mutation的载荷(payload)。
//...
mutations: {
/**
* @method:只传入state,修改loading状态
* @param {bool} isLoading:loading状态
*/
changeLoading(state) {
state.isLoading = !state.isLoading
}
}
store.commit('changeLoading') mutations: {
/**
* @method:传入state和payload,修改loading状态
* @param {bool} isLoading:loading状态
*/
changeLoading(state,payload) {
state.isLoading = payload.isLoading
}
}
store.commit('changeLoading',{isLoading: true})
还有一种提交mutation的方式是直接使用包含 type 属性的对象,不过我不是很推荐这样的方式,因为用上面的方式来处理的话,代码的易读性会更高。
store.commit({
type: 'changeLoading',
isLoading: true
})
4.mutation-types.js
在需要多人协作的项目中,我们可以使用常量代替mutation 事件类型。这在各种 Flux 实现中是很常见的模式。同时把这些常量放在单独的文件中可以让协作开发变得清晰。
// mutation-types.js
export const CHANGE_LOADING= 'CHANGE_LOADING' // mutation.js
import { CHANGE_LOADING} from './mutation-types' export default{
[CHANGE_LOADING](state,payload){
state.isLoading = payload.isLoading
},
}
对于定义mutation-type里面的事件类型,我大致遵循我自己定义的如下规范:
1、因为mutation类似于事件,所以以动词开头
2、单词间以下划线进行连接
3、保存到vuex里面的状态用RECORD标识
4、缓存到本地的状态用SAVE标识
当然,这个规范的话大家可以自己定义,只要能通过mutation-type就能知道mutation的意图就是极好的。
5.Getter(getter.js)
有时候我们需要从 store 中的 state 中派生出一些状态,例如我上面提到的需要在异步请求的时候去显示一个带有遮罩层的loading,然后我loading的下面需要根据state去展示loading的状态。在不使用getter的情况下,我们会选择使用计算属性去处理。
computed: {
loadingTxt () {
return this.$store.state.isLoading ? '加载中' : '已完成';
}
}
但是,我们这个loading需要在很多的组件中去使用它。那么,我们要么复制这个函数,要么抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。
如果使用Getter,那么一切都变得美好了。
//getter.js
export default {
loadingTxt:(state) =>{
return state.isLoading ? '加载中' : '已完成';
}
};
就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
并且,Getter 也可以接受其他 getter 作为第二个参数:
export default {
loadingTxt:(state) =>{
return state.isLoading ? '加载中' : '已完成';
},
isLoading:(state,getters) => {
return 'string' === typeof getters.loadingTxt ? true : false;
}
};
通过mapGetters辅助函数可以将 store 中的 getter 映射到局部计算属性
//组件中
import { mapGetters } from 'vuex' export default {
data(){
return {
//...
}
},
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'loadingTxt',
'isLoading',
// ...
])
}
}
6.Action(action.js)
action的功能和mutation是类似的,都是去变更store里的state,不过action和mutation有两点不同:
1、action主要处理的是异步的操作,mutation必须同步执行,而action就不受这样的限制,也就是说action中我们既可以处理同步,也可以处理异步的操作
2、action改变状态,最后是通过提交mutation
就拿购物车来说,当我们去添加一个商品的时候,我们需要先和后台去通讯一次,这里涉及到sku或者说是如果用户只添加了但是没有去下单。
如果后台添加成功,前端再去展示新添加的商品,如果失败,我们需要告诉用户添加失败了。
const actions = {
46 checkout ({
47 state,
48 commit,
49 //rootState
50 }, products) {
51 const savedCartItems = [...state.added]
52 commit(SET_CHECKOUT_STATUS, null)
53 // 置空购物车
54 commit(SET_CART_ITEMS, { items: [] })
55 shop.buyProducts(
56 products,
57 //成功
58 () => commit(SET_CHECKOUT_STATUS, 'successful'),
59 //失败
60 () => {
61 commit(SET_CHECKOUT_STATUS, 'failed')
62 commit(SET_CART_ITEMS, { items: savedCartItems })
63 }
64 )
65 }
66 }
7.module
当我们的项目足够大的时候,单一的状态树这个时候就会显得很臃肿了。因为需要用vuex进行状态管理的状态全部集中在一个state对象里面。
所以,当一个东西大了以后,我们就要想办法进行分割,同样的道理,我们熟知的分冶法和分布式其实也是基于这样的一个思想在里面。而vuex提供了module,我们就可以去横向的分割我们的store。
比如说,我在项目中需要去做一个购物车这样的东西,这在电商的项目中也是常见的需求。
//shopCart.js
import shop from '../../api/shop'
import {
ADD_TO_CART,
SET_CART_ITEMS,
SET_CHECKOUT_STATUS
} from '../mutation-types' const state = {
added: [],
checkoutStatus: null
} /**
* module getters
* @param {Object} state:模块局部state
* @param {Object} getters:模块局部getters,会暴露到全局
* @param {Object} rootState:全局(根)state
*/
const getters = {
checkoutStatus: state => state.checkoutStatus,
cartProducts: (state, getters, rootState) => {
return state.added.map(({ id, quantity }) => {
const product = rootState.products.all.find(product => product.id === id)
return {
title: product.title,
price: product.price,
quantity
}
})
},
cartTotalPrice: (state, getters) => {
return getters.cartProducts.reduce((total, product) => {
return total + product.price * product.quantity
}, 0)
}
} /**
* module actions
* @param {Object} state:模块局部state
* @param {Object} getters:模块局部getters,会暴露到全局
* @param {Object} rootState:全局(根)state
*/
const actions = {
checkout ({
state,
commit,
//rootState
}, products) {
const savedCartItems = [...state.added]
commit(SET_CHECKOUT_STATUS, null)
// 置空购物车
commit(SET_CART_ITEMS, { items: [] })
shop.buyProducts(
products,
//成功
() => commit(SET_CHECKOUT_STATUS, 'successful'),
//失败
() => {
commit(SET_CHECKOUT_STATUS, 'failed')
commit(SET_CART_ITEMS, { items: savedCartItems })
}
)
}
} /**
* module mutations
* @param {Object} state:模块局部state
* @param payload:mutation的载荷
*/
const mutations = {
//用id去查找商品是否已存在,
[ADD_TO_CART] (state, { id }) {
state.checkoutStatus = null
const record = state.added.find(product => product.id === id)
if (!record) {
state.added.push({
id,
quantity: 1
})
} else {
record.quantity++
}
},
[SET_CART_ITEMS] (state, { items }) {
state.added = items
},
[SET_CHECKOUT_STATUS] (state, status) {
state.checkoutStatus = status
}
} export default {
state,
getters,
actions,
mutations
}
在module的定义的局部state,getters,mutation,action中,后三个都会暴露到全局的store中去,这样使得多个模块能够对同一 mutation 或 action 作出响应。就不需要在其他的模块中去定义相同的mutation或action了。
而这里的state是局部的。这也导致后来的持久化无法去处理用module分割后的state。
如同上面的module =》shopCart,
当我们无论是在index.js里面或者其他的module中,shopCart里面的getters或者action或者mutations,我们都可以去使用。
//test.js
const state = {
isTest:false
}; const getters = {
isTest :state => state.isTest,
checkTestStatus:(state,getters) => {
return getters.checkoutStatus;
}
}; export default {
state,
getters,
}
//组件中
...mapGetters([
'checkTestStatus'
])
//...
created(){
this.checkTestStatus ;//null
}
如果说,我就想让我的module里面的定义的全部都是独享的。我们可以使用module的命名空间,通过设置namespaced: true。
//test.js
const getters = {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // 'test/someOtherGetter'
rootGetters.someOtherGetter // 'someOtherGetter'
},
someOtherGetter: state => { ... }
}; const actions = {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // 'test/someGetter'
rootGetters.someGetter // 'someGetter' dispatch('someOtherAction') // 'test/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // 'someOtherAction' commit('someMutation') // 'test/someMutation'
commit('someMutation', null, { root: true }) // 'someMutation'
},
someOtherAction ({ state,commit }, payload) { ... }
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
8.持久化state的工具:vuex-persistedstate
用过vuex的肯定会有这样一个痛点,就是刷新以后vuex里面存储的state就会被浏览器释放掉,因为我们的state都是存储在内存中的。
而像登录状态这样的东西,你不可能一刷新就让用户重新去登录吧!所以,我们会去选择把状态存储到本地。
这样一来问题貌似是解决了,但是当我们需要使用的时候,我们就需要不断的从本地,通过getStore这样的方法去取得我们state。如果需要更新的话,我们又要在mutation里面通过setStore这样的方法去处理它。
虽然,我们的setStore都是在操作了state以后再去调用的,也就是说无论是通过vuex的logger或者vue的dev tool我们都是可以对local里面的状态进行跟踪的,但是,我们无法保证我们每次都记着去写setStore。
这样一来,在共享state的组件中,我们的代码可能就会是这样的。
import { getStore } from '@/util'
//组件中
mounted(){
this.foo = getStore('foo');
this.bar = getStore('bar');
//....
}
那么,如何去改进呢?
我们能想到的就是,能不能让state不是保存在内存中,而是存储在本地。
而vuex-persistedstate做了这样的事情,它帮我们将store里面的state映射到了本地环境中。这样一来,我通过提交mutation改变的state,会动态的去更新local里面对应的值。
大家感兴趣的话,可以戳这里,这里有个小dmeo
vuex的使用及持久化state的方式的更多相关文章
- vuex的使用及持久化state的方式详解
vuex的使用及持久化state的方式详解 转载 更新时间:2018年01月23日 09:09:37 作者:baby格鲁特 我要评论 这篇文章主要介绍了vuex的使用及持久化state的方 ...
- FluentData - 轻量级.NET ORM持久化技术解决方式
FluentData - 轻量级.NET ORM持久化技术解决方式 文件夹: 一.什么是ORM? 二.使用ORM的优势 三.使用ORM的缺点 四.NET下的ORM框架有哪些? 五.几 ...
- (八)Redis之持久化之AOF方式
一.概念 AOF方式:将以日志,记录每一个操作 优势:安全性相对RDB方式高很多: 劣势:效率相对RDB方式低很多: 二.案例 appendonly no默认关闭aof方式 我们修改成yes 就开启 ...
- [转] Vuex入门(2)—— state,mapState,...mapState对象展开符详解
1.state state是什么? 定义:state(vuex) ≍ data (vue) vuex的state和vue的data有很多相似之处,都是用于存储一些数据,或者说状态值.这些值都将被挂载 ...
- redis8--数据持久化两种方式
持久化功能redis为了内部数据的安全考虑,会把本身的数据以文件形式保存到硬盘中一份,在服务器重启之后会把硬盘中的数据恢复到内存(redis)的里边.数据保存到硬盘的过程就称为"持久化&qu ...
- Redis持久化磁盘IO方式及其带来的问题 有Redis线上运维经验的人会发现Redis在物理内存使用比较多,但还没有超过实际物理内存总容量时就会发生不稳定甚至崩溃的问题,有人认为是基于快照方式持
转自:http://blog.csdn.net/kaosini/article/details/9176961 一.对Redis持久化的探讨与理解 redis是一个支持持久化的内存数据库,也就是 ...
- Redis的持久化之RDB方式
RDB方式 Redis是默认支持的 优势:只有一个文件,时间间隔的数据,可以归档为一个文件,方便压缩转移(就一个文件) 劣势:如果宕机,数据损失比较大,因为它是没一个时间段进行持久化操作的.也就是积攒 ...
- Redis持久化磁盘IO方式及其带来的问题
有Redis线上运维经验的人会发现Redis在物理内存使用比较多,但还没有超过实际物理内存总容量时就会发生不稳定甚至崩溃的问题 一.对Redis持久化的探讨与理解 redis是一个支持持久化的内存数据 ...
- vuex源码分析(二) state及strict属性 详解
state也就是vuex里的值,也即是整个vuex的状态,而strict和state的设置有关,如果设置strict为true,那么不能直接修改state里的值,只能通过mutation来设置 例1: ...
随机推荐
- redis hash结构 遍历某一个key下所有的(field,values)的方法
本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/95 redis的hash结构中存储了如下的数据: $input ...
- JS画几何图形之一【直线】
JS画图的想法经过大脑的时候,觉得有点意思,所以就实践了一番.JS画图为系列文章,本是讲点.线和面 先看样例:http://www.zhaojz.com.cn/demo/draw5.html 一.点 ...
- SpringMVC底层数据传输校验的方案
团队的项目正常运行了很久,但近期偶尔会出现BUG.目前观察到的有两种场景:一是大批量提交业务请求,二是生成批量导出文件.出错后,再执行一次就又正常了. 经过跟踪日志,发现是在Server之间进行jso ...
- Illustration of Git branching and merge
网上看到的描述Git工作流程的图片,有些出处忘了保存,仅供学习. 1. One Git Branching Model 出处: http://nvie.com/posts/a-successful-g ...
- MySQL 的调节和优化的提示
MySQL 服务器硬件和操作系统调节: 1. 拥有足够的物理内存来把整个InnoDB文件加载到内存中——在内存中访问文件时的速度要比在硬盘中访问时快的多.2. 不惜一切代价避免使用Swap交换分区 – ...
- [编织消息框架][netty源码分析]14 PoolChunk 的 PoolSubpage
final class PoolSubpage<T> implements PoolSubpageMetric { //该page分配的chunk final PoolChunk<T ...
- 【转】java jvm 线程 与操作系统线程
原文链接:http://segmentfault.com/q/1010000000370403 Java的目标是要跨平台,而不同的操作系统(如类Unix和Windows)其任务调度机制有很大的不同,故 ...
- Kafka的基本概念与安装指南(单机+集群同步)
最近在搞spark streaming,很自然的前端对接的就是kafka.不过在kafka的使用中还是遇到一些问题,比如mirrormaker莫名其妙的丢失数据[原因稍后再说],消费数据offset错 ...
- python模块安装报错 :error: command 'gcc' failed with exit status 1
参考:http://blog.csdn.net/fenglifeng1987/article/details/38057193 解决方法 yum install gcc libffi-devel py ...
- python词云的制作方法
第一次接触到词云主要是觉得很好看,就研究了一下,官方给出了代码的,但是新手看的话还是有点不容易,我们来尝试下吧. 环境:python2.7 python库:PIL(pillow),numpy,matp ...