前端【VUE】17-状态管理【Vuex】【使用介绍】【分模块】【持久化到localStorage【插件vuex-persist】】
1、组件下载
vue 与vuex的版本对应关系:Vue 2 匹配的 Vuex 3
Vue 3 匹配的 Vuex 4
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
官方网站:https://vuex.vuejs.org/zh/
1 // npm 2 npm install vuex@next --save 3 4 // yarn 5 yarn add vuex@next --save
2、项目中定义及配置
在src目录下新增store目录,创建index.js文件,内容如下
1 // 1. 导入所需的包 2 import Vue from 'vue' 3 import Vuex from 'vuex' 4 // 2. 将Vuex注册为Vue的插件 5 Vue.use(Vuex) 6 // 3. 创建 store 实例对象 7 const store = new Vuex.Store({ 8 // 配置vuex 9 strict: true, // 开启严格模式,防止小白在组件中使用的时候直接修改state数据 10 // state 用于存储数据(存储状态) (vuex状态管理) 11 state: { 12 sex: 23, 13 uname: 'badWoman', 14 list: [ 15 { id: 1, name: '吃饭', isDone: true }, 16 { id: 2, name: '睡觉', isDone: false }, 17 { id: 3, name: '听歌', isDone: true } 18 ] 19 }, 20 // mutations 是 vuex 中"唯一"可以修改 state 数据的方法, 不支持异步更新,只能放同步代码 21 mutations: { 22 abc (state, n) { 23 state.sex = n 24 } 25 }, 26 // actions 里面放异步方法 27 actions: { 28 newSex (store, n) { 29 setTimeout(() => { 30 store.commit('abc', n) // 只要是修改state中内容, 都通过mutations中的方法进行操作 31 }, 3000) 32 } 33 }, 34 // getters 是vuex中的计算属性(和组件中的计算属性意义一样,但是不支持set修改) 35 // 为了方便获取state中的数据;插件作者会给每个计算属性方法,传递一个 state 参数 36 getters: { 37 ccc (state) { 38 return state.sex * state.sex 39 }, 40 all (state) { 41 return state.list.every(item => item.isDone === true) 42 } 43 } 44 }) 45 // 4. 导出 store 对象 46 export default store
main.js
1 import Vue from 'vue' 2 import App from './App.vue' 3 // 导入定义的store目录下的index.js文件 4 // import store from '@/store/index.js' // 如果导入某个目录下的index文件, 则可以省略index 5 import store from '@/store' 6 Vue.config.productionTip = false 7 8 new Vue({ 9 store, // 将导入的store添加到vue实例 10 render: h => h(App) 11 }).$mount('#app')
组件AnyOne.vue
1 <template> 2 <div> 3 <h2>随便一个组件-1</h2> 4 <!-- 使用vuex中状态, state中的uname --> 5 <p>{{ $store.state.uname }}</p> 6 <!-- 使用vuex中状态, state中的sex --> 7 <p>{{ $store.state.sex }}</p> 8 <!-- 使用vuex中配置的计算属性 --> 9 <p>{{ $store.getters.ccc }}</p> 10 <p>{{ $store.getters.all }}</p> 11 12 <!-- 调用mutations中定义的修改state的方法abc, 并传递参数5000 --> 13 <button @click="$store.commit('abc', 5000)">更新sex</button> 14 <br /> 15 <!-- 调用actions中定义的异步方法newSex,并传递参数20 --> 16 <button @click="$store.dispatch('newSex', 20)">3s后更新sex</button> 17 </div> 18 </template> 19 20 <script> 21 export default {} 22 </script> 23 <style lang="less" scoped></style>
组件AnyTwo.vue
1 <template> 2 <div> 3 <h2>随便一个组件-2</h2> 4 <!-- 使用计算属性 --> 5 <!-- <P>{{ sex }}</P> --> 6 <p>{{ uname }}</p> 7 <P>{{ nianLing }}</P> 8 <P>{{ uname }}</P> 9 <P>{{ ccc }}</P> 10 <P>{{ all }}</P> 11 <button @click="abc(100)">更新年龄</button> 12 <br /> 13 <button @click="newSex(200)">三秒后更新年龄</button> 14 </div> 15 </template> 16 17 <script> 18 // 导入vuex中提供的一些方法 19 import { mapState, mapGetters, mapMutations, mapActions } from 'vuex' 20 export default { 21 // data () { 22 // return { 23 // sex: 1777 24 // } 25 // }, 26 computed: { 27 // mapState提供一种简写方式: ...mapState() 传想要获取的状态信息, 拿到当前页面作为计算属性使用 28 // ...mapState(['sex', 'uname', 'list']), // 数组写法 29 30 // 给从state中获取的状态取别名,防止与当前页面重名,作为当前页面的计算属性 31 ...mapState({ nianLing: 'sex', uname: 'uname', list: 'list' }), // 对象写法, 相当于取别名, 防止与data中的变量重名 32 33 // 将vuex中的getters拿到当前页面作为当前页面的计算属性 34 ...mapGetters(['ccc', 'all']) 35 }, 36 methods: { 37 // 将vuex中mutations定义的方法拿到当前页面作为当前页面的方法 38 ...mapMutations(['abc']), 39 ...mapActions(['newSex']) 40 } 41 } 42 </script> 43 <style lang="less" scoped></style>
根组件App.vue
1 <template> 2 <div> 3 <AnyOne></AnyOne> 4 <hr /> 5 <AnyTwo></AnyTwo> 6 </div> 7 </template> 8 9 <script> 10 import AnyOne from './components/AnyOne.vue' 11 import AnyTwo from './components/AnyTwo.vue' 12 export default { 13 data () { 14 return {} 15 }, 16 components: { 17 AnyOne, 18 AnyTwo 19 } 20 } 21 </script> 22 <style lang="less" scoped></style>
3、使用
4、简化调用数据
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'通过vuex中的一些方法将定义的store中的内容拿到当前页面
5、分模块
在store目录下新建modules目录,用来存放各个模块的状态信息分模块之后,在使用就需要加上模块名称,例如:$store.state.cart.list 获取cart模块中的list状态,调用方法也都是一样要加模块名的
src/store/modules/cart.js
1 import axios from 'axios' 2 export default { 3 namespaced: true, //必须写 4 // state 相当于vue中的data 5 state: { 6 list: [] 7 }, 8 // vueX中只能通过 mutations 修改数据 mutations 里边只能放同步方法 9 mutations: { 10 updateList (state, aaa) { 11 state.list = aaa 12 }, 13 finalList (state, obj) { 14 let row = state.list.find(item => item.id === obj.id) 15 row.count = obj.count 16 } 17 }, 18 //vueX 的计算属性 不支持set修改 19 getters: { 20 total (state) { 21 return state.list.reduce((x, y) => x + y.count, 0) 22 }, 23 newPrice (state) { 24 return state.list.reduce((x, y) => x + y.count, 0) 25 } 26 }, 27 // actions 中调用 mutations 方法 因为 mutations 是vueX中修改数据的唯一方法且只能写同步方法,而 actions 可以放异步方法 28 actions: { 29 // 发送ajax请求 获取全部数据 30 async getList (store) { 31 const { data: res } = await axios.get('http://localhost:3000/list') 32 // 调用 mutation 方法,更新list数据 33 store.commit('updateList', res) 34 }, 35 async updateList (store, obj) { 36 // 一 更新服务器数据 37 await axios.patch(`http://localhost:3000/list/${obj.id}`, obj) 38 // 二 更新 vuex 数据 39 store.commit('finalList', obj) 40 } 41 } 42 }
src/store/index.js
1 import Vue from 'vue' 2 import Vuex from 'vuex' 3 4 // 导入小模块 5 import cart from './modules/cart' 6 7 Vue.use(Vuex) 8 9 export default new Vuex.Store({ 10 modules: { 11 // 模块名:导入的变量名 12 // 'cart': cart 13 cart // 模块名和变量名一致可以简写 14 } 15 })
main.js
1 import Vue from 'vue' 2 import App from './App.vue' 3 import store from './store' // 导入store下的index.js 如果导入某个模块的index.js文件, 则可以省略 4 5 Vue.config.productionTip = false 6 7 new Vue({ 8 store, // 注册到vue中 9 render: h => h(App) 10 }).$mount('#app')
根组件App.vue
1 <template> 2 <div class="app-container"> 3 <!-- Header 区域 --> 4 <cart-header></cart-header> 5 6 <!-- 商品 Item 项组件 --> 7 <cart-item 8 v-for="item in $store.state.cart.list" // 遍历模块cart中的数据, 模块中的list数据已经在created中的方法中异步更新了 9 :key="item.id" 10 :item="item" 11 > 12 <!-- 因为 vuex 是数据共享,可以不循环 不传值 其他组件也可以使用 --> 13 </cart-item> 14 15 <!-- Foote 区域 --> 16 <cart-footer></cart-footer> 17 </div> 18 </template> 19 20 <script> 21 import CartHeader from '@/components/cart-header.vue' 22 import CartFooter from '@/components/cart-footer.vue' 23 import CartItem from '@/components/cart-item.vue' 24 25 export default { 26 name: 'App', 27 components: { 28 CartHeader, 29 CartFooter, 30 CartItem 31 }, 32 created () { // 在这里调用cart模块中的异步方法 33 this.$store.dispatch('cart/getList') // 如果有参数传递, 则this.$store.dispatch('模块名/模块内actions中定义的方法', 方法所需的参数) 34 } 35 } 36 </script> 37 38 <style lang="less" scoped> 39 .app-container { 40 padding: 50px 0; 41 font-size: 14px; 42 } 43 </style>
6、持久化存储到localStorage
存储token到vuex
之前,是将token存储到 localStorage 中。
这个项目将 token 存储到 vuex 仓库中,原因是:
- token在其他组件、模块中都会用得到,属于公共数据
- vuex 的仓库在内存中,读取、更新速度非常快
- 有完备的 语法来操作 vuex仓库中的数据
【1】新建 store/modules/user.js
1 export default { 2 namespaced: true, // 开启命名空间 3 state: { 4 token: '' 5 }, 6 mutations: { 7 updateToken (state, val) { 8 state.token = val 9 } 10 } 11 }
【2】store/index.js 中导入模块,并注册模块
1 import Vue from 'vue' 2 import Vuex from 'vuex' 3 import user from '@/store/modules/user' 4 Vue.use(Vuex) 5 6 export default new Vuex.Store({ 7 // strict: true, // 此项可加可不加 8 modules: { user } 9 })
【3】登录成功后,将token存储到 vuex 仓库
1 this.$store.commit('user/updateToken', res.data.token)
【4】退出的时候,移除token(修改token的值为空)
1 this.$store.commit('user/updateToken', '')
【5】请求拦截器,获取token
1 // 先导入store 2 import store from '@/store' 3 4 // 请求拦截器中,带请求头这里 5 config.headers.Authorization = 'Bearer ' + store.state.user.token
【6】响应拦截器,如果是401错误,移除token
1 store.commit('user/updateToken', '')
【7】检查,登录后,尝试使用 vue 调试工具,查看 vuex 中是否有token。
持久化存储token
上述存储token,也有一定的问题,当页面刷新后,vuex 中的数据就会重置(毕竟是存在内存中),所以我们还需要将 vuex仓库中的数据,同步到 localStorage 中。这样数据就可以持久化存储了。
这里的实现方案有两个,一是自己写代码。二是使用插件。
我们选择使用插件:vuex-persist ,类似的插件还有 vuex-along 等等,非常多。
具体使用步骤:
【1】安装模块
1 // pnpm方式 2 pnpm add vuex-persist 3 // npm方式 4 npm i vuex-persist
【2】store/index.js 中,导入,创建对象,并设置为插件。
1 import Vue from 'vue' 2 import Vuex from 'vuex' 3 import VuexPersistence from 'vuex-persist' 4 import user from '@/store/modules/user' 5 Vue.use(Vuex) 6 const vuexLocal = new VuexPersistence({ 7 storage: window.localStorage 8 }) 9 export default new Vuex.Store({ 10 // strict: true, // 此项可加可不加 11 modules: { user }, 12 plugins: [vuexLocal.plugin] 13 })
设置完毕。
-
登录成功后
- 会将 vuex 仓库的数据存储到 localStorage 中
-
刷新页面后
- 会将 localStorage 中的数据恢复到 vuex仓库中