一、Vuex 是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方生态系统中,并且在开发大型、复杂单页应用(SPA)时尤其有用。
二、核心概念
- state:定义应用程序状态的数据。(数据,类似data)
- mutations:用于修改状态的方法。(执行,类似methods)
- actions:用于触发 mutations 的方法。(事件,类似controller)
- getters:用于获取 state 中的数据。(数据加工,类似computed)
- modules:使用单一状态树,致使应用的全部状态集中到一个很大的对象,所以把每个模块的局部状态分装使每一个模块拥有本身的 state、mutation、action、getters、甚至是嵌套子模块;
三、Vuex 的工作流程
- 通过dispatch去提交一个actions,
- actions接收到这个事件之后,在actions中可以执行一些异步|同步操作,根据不同的情况去分发给不同的mutations,
- actions通过commit去触发mutations,
- mutations去更新state数据,
- state更新之后,就会通知vue进行渲染
四、什么情况下我应该使用 Vuex?
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式 (opens new window)就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
五、Vuex 的使用
- 定义 state:定义应用程序的状态数据。
- 定义 mutations:定义修改 state 的方法。
- 定义 actions:定义触发 mutations 的方法,可以包含异步操作。
- 定义 getters:定义获取 state 中数据的方法。
- 创建 store:将 state、mutations、actions、getters 组合成一个 store 对象。
- 在应用程序中使用 store:在应用程序中使用 store 对象的 state、mutations、actions、getters。
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。
六、使用示例
以购物车为例,介绍vuex的使用全过程。
// vuex的入口文件,封装了cart模块,直接引入使用
// store/index.js
import Vue from 'vue' import Vuex from 'vuex' import cart from './cart' Vue.use(Vuex) export default new Vuex.Store({ modules: {cart} })
// cart模块的首页,引入了getters,actions,mutations
// 以及state中模拟的数据
// store/cart/index.js
import getters from "./getters" import actions from "./actions" import mutations from "./mutations" export default { state:{ goods: [{ id:1, name:'小米手机', price: 2000, count: 0, },{ id:2, name:'苹果手机', price: 3000, count: 0, },{ id:3, name:'华为手机', price: 4000, count: 0, }], }, getters, actions, mutations }
// getters文件,计算购物车的总价及数量
// store/cart/getters.js
export default { totalPrice(state){ return state.goods.reduce((total,item)=>{ total += item.price * item.count return total },0).toFixed(2) }, count(state){ return state.goods.reduce((total,item)=>{ total += item.count return total },0) } }
// actions文件,用于提交商品数量的变化
// store/cart/actions.js
export default { add({commit},payload){ commit('add',payload) }, reduce({commit},payload){ commit('reduce',payload) } }
// mutations文件,用于改变state中商品的数量变化
// store/cart/mutations.js
export default { add(state,index){ state.goods[index].count++ }, reduce(state,index){ if(state.goods[index].count>=1){ state.goods[index].count-- } }, }
// 这里是使用vuex的页面,展示购物车页面
// app.vue
<template> <div class="about"> <div v-for="(item, index) in goods" :key="index"> <div>{{ item.name }}</div> <div>{{ item.price }}</div> <div style="display: flex"> <button @click="reduce(index)">-</button> <div>{{ item.count }}</div> <button @click="add(index)">+</button> </div> </div> <div style="display: flex;"> <div>总计:{{ count }}</div> <div>结算:{{ totalPrice }}</div> </div> </div> </template> <script> import { mapGetters,mapState } from 'vuex' export default { computed: { ...mapState({ goods(state) { return state.cart.goods }, }), ...mapGetters(['count', 'totalPrice']) }, methods: { add(index) { this.$store.dispatch('add', index) }, reduce(index) { this.$store.dispatch('reduce', index) } } } </script>
七、状态持久化
vuex优势: 相比sessionStorage,存储数据更安全,sessionStorage可以在控制台被看到。
vuex劣势: 在F5刷新页面后,vuex会重新更新state,所以,存储的数据会丢失。(即刷新浏览器,vuex数据丢失)
1、手动利用HTML5的本地存储
vuex的state在localStorage或sessionStorage或其它存储方式中取值 在mutations定义的方法里对vuex的状态操作的同时对存储也做对应的操作。这样state就会和存储一起存在并且与vuex同步
2、利用vuex-persistedstate插件
2.1、安装
npm install vuex-persistedstate
2.2、配置
- vuex-persistedstate默认存储于localStorage
// 在store/index.js import Vue from 'vue'; import Vuex from 'vuex'; import createPersistedState from 'vuex-persistedstate'; Vue.use(Vuex); export default new Vuex.Store({ // ... plugins: [createPersistedState()] // ... });
- 存储sessionStorage的情况
plugins: [ createPersistedState({ storage: window.sessionStorage }) ]
- 存储cookie的情况
import Cookies from 'js-cookie' plugins: [ persistedState({ storage: { getItem: key => Cookies.get(key), setItem: (key, value) => Cookies.set(key, value, { expires: 7 }), removeItem: key => Cookies.remove(key) } }) ]
指定需要持久化的state
在vuex-persistedstate中,如果你想要指定需要持久化的state(即只持久化Vuex store中的部分状态,而不是全部),你可以通过paths配置选项来实现。paths是一个数组,其中的每个元素都是一个字符串,表示Vuex store中需要被持久化的状态的路径。
import Vue from 'vue' import Vuex from 'vuex' import createPersistedState from 'vuex-persistedstate' import cart from './cart' import user from './user' Vue.use(Vuex) export default new Vuex.Store({ plugins: [ createPersistedState({ paths:['cart','userSettings','user.name'] // 持久化'cart'模块的全部状态 // 持久化全局状态中的'userSettings' // 只持久化'user'模块中的'name' }) ], state: { // 你的全局状态 token: '', userSettings: { theme: 'dark', notifications: true } }, // 你的模块 modules: {cart,user} })
过滤需要持久化的state
在vuex-persistedstate中,reducer是一个可选的配置项,它的作用是对即将被持久化到存储(如localStorage或sessionStorage)中的Vuex状态进行过滤或转换。通过使用reducer,你可以控制哪些数据被保存,以及如何保存这些数据,从而实现更细粒度的状态持久化控制。
reducer通常是一个函数,它接收当前的state(或特定部分的state,取决于你的配置)作为参数,并返回一个新的对象,该对象包含了应该被持久化的数据。
plugins: [ createPersistedState({ storage: window.sessionStorage, reducer(state) { // 只返回需要被持久化的部分state return { // 假设我们只想持久化user模块中的name和avatar字段 user: { name: state.user.name, avatar: state.user.avatar } }; } }) ]
如果path和reducer同时存在则使用reducer, 忽悠paths属性。
- API 配置
createPersistedState([options])使用给定的选项创建插件的新实例。可以提供以下选项来配置您的特定需求的插件:
API | 参数类型 | 说明 | 默认值 |
key | String | 用于存储持久状态的键。 | vuex |
paths | Array | 部分保持状态的任何路径的数组。如果没有给定路径,则完整状态将持久化。如果给定一个空数组,则不会持久化任何状态。如果使用模块,请包括模块名称。 | [] |
reducer | Function | 一个将被调用的函数,对即将被持久化到存储中的Vuex状态进行过滤或转换。 | 都包含这些值 |
subscriber | Function | 被调用以设置突变订阅的函数。 | store => handler => store.subscribe(handler) |
storage | String | 指定存储数据的方式。 | localStorage |
getState | Function | 用来重新补充先前持久状态的功能。 | storage |
setState | Function | 用以保持给定状态的函数。 | storage |
filter | Function | 一个将被调用以过滤setState最终将在存储中筛选过滤的函数。 | () => true |
注意,getState 和 setState 是高级选项,它们允许你完全控制状态的存储和恢复过程。然而,在大多数情况下,简单地使用 paths 或默认行为可能就足够了。