前端【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 仓库中,原因是:

  1. token在其他组件、模块中都会用得到,属于公共数据
  2. vuex 的仓库在内存中,读取、更新速度非常快
  3. 有完备的 语法来操作 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仓库中
posted @ 2024-04-08 22:12  为你编程  阅读(49)  评论(0编辑  收藏  举报