关键词搜索

源码搜索 ×
×

状态管理 - 全局状态管理工具

发布2022-01-03浏览1079次

详情内容

一、单向数据流
1. 理念示意图

“单向数据流” 理念示意图
在这里插入图片描述

2. 简述
  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。

但是,当我们的应用遇到**多个组件共享状态(数据)**时,单向数据流的简洁性很容易被破坏(回忆 search-blogsearch-history 的代码):

  • 多个视图依赖于同一状态(数据)。
  • 来自不同视图的行为需要变更同一状态(数据)。

所以我们不得不通过 父子组件传递数据 的方式,来频繁的修改状态(数据)。但是这种方式是 非常脆弱,通常会导致无法维护的代码。

二、什么是全局状态管理模式

所以我们就需要就想到了一种方案,我们把:

把多个组件之间共用的数据抽离出来,通过一个 单例模式 进行管理,而这种管理的方式就叫做【全局状态管理模式】。

具备 【全局状态管理模式】 的库,就是 【全局状态管理工具】

而在 vue 中存在一个专门的 【全局状态管理工具】,叫做 vuex

因为 uniapp 追随 vue微信小程序 的语法,所以我们可以在 uniapp 中使用 vuex 来进行 【全局状态管理】,并且这是一个 非常被推荐 的选择。

三、重点概念
3.1. 什么是全局状态管理模式?

模式:把多个组件之间共用的数据抽离出来,通过一个 单例模式 进行管理。

3.2.全局状态管理工具?

工具:具备 【全局状态管理模式】 的库

3.3. 什么是 vuex

vue 应用程序进行全局状态管理的工具

四、在项目中导入 vuex
4.1. 状态管理配置

在项目根目录下创建store文件夹,并在store文件夹下创建index.js
内容如下:

// 1. 导入 Vue 和 Vuex
import Vue from 'vue';

// uniapp 已默认安装,不需要重新下载
import Vuex from 'vuex';

// 2. 安装 Vuex 插件
Vue.use(Vuex);

// 3. 创建 store 实例
const store = new Vuex.Store({});

export default store;
    4.2. 注册vuex

    在 main.js 中注册 vuex 插件

    // 导入 vuex 实例
    import store from './store';
    ...
    
    const app = new Vue({
      ...App,
      store // 挂载实例对象
    });
    app.$mount();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    五、测试 vuex 是否导入成功

    ,可以按照模块单独配置,然后,在index.js中引入,来进行对各模块的统一状态管理

    5.1. 创建搜索模块

    在store文件夹下,创建modules文件夹,并在此文件夹下创建search模块(search.js)
    内容如下:

    export default {
      // 独立命名空间
      namespaced: true,
      // 通过 state 声明数据
      state: () => {
        return {
          msg: 'hello vuex'
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    5.2. 注入模块

    index.js 中注入模块

    ...
    // 导入 search.js 暴露的对象
    import search from './modules/search';
    
    // 2. 安装 Vuex 插件
    Vue.use(Vuex);
    // 3. 创建 store 实例
    const store = new Vuex.Store({
      modules: {
        search
      }
    });
    export default store;
    
      5.3. 页面使用模块中的数据

      有3步:

      1. 导入 mapState 函数
      2. 在 computed 中,通过 mapState 函数,注册 state 中的数据,导入之后的数据可直接使用(就像使用 data 中的数据一样)
        // mapState(模块名, [‘字段名’,‘字段名’,‘字段名’])
      3. 使用导入的 vuex 模块中的数据

      index.vue 中使用 模块中的数据

      <template>
        <view class="search-blog-container">
          <!-- 3. 使用导入的 vuex 模块中的数据 -->
          <view>{{ msg }}</view>
          ...
        </view>
      </template>
      
      <script>
      // 1. 导入 mapState 函数
      import { mapState } from 'vuex';
      ...
      export default {
       ...
        computed: {
          // 2. 在 computed 中,通过 mapState 函数,注册 state 中的数据,导入之后的数据可直接使用(就像使用 data 中的数据一样)
          // mapState(模块名, ['字段名','字段名','字段名'])
          ...mapState('search', ['msg'])
        }
      };
      </script>
      
        14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      5.4. 构建 search 模块

      search.js

      export default {
        // 独立命名空间
        namespaced: true,
        // 通过 state 声明数据
        state: () => ({
          searchData: []
        }),
        // 更改 state 数据的唯一方式是:提交 mutations
        mutations: {
          /**
           * 添加数据
           */
          addSearchData(state, val) {
            if (!val) return;
            const index = state.searchData.findIndex((item) => item === val);
            if (index !== -1) {
              state.searchData.splice(index, 1);
            }
            state.searchData.unshift(val);
          },
          /**
           * 删除指定数据
           */
          removeSearchData(state, index) {
            state.searchData.splice(index, 1);
          },
          /**
           * 删除所有数据
           */
          removeAllSearchData(state) {
            state.searchData = [];
          }
        }
      };
      
      
        14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      5.5. 对vuex数据操作

      对vuex数据操作步骤:

      1. 导入mapMutations 函数,处理mutation的问题
      2. 利用 mapMutations函数,可以直接把vuex中的mutation合并到当前组件的methods中,合并之后,使用mutation就像使用methods中的方法一样
      <template>
        <view class="hot-container">
      
          <view v-for="(item, index) in tabData" :key="index">
            {{ item.label }} - {{ index }}
          </view>
        </view>
      </template>
      
      <script>
      // 1. 导入 mapState 函数
      // 2. 导入mapMutations 函数,处理mutation的问题
      import { mapState, mapMutations } from 'vuex';
      import { getHotTabs } from 'api/hot';
      export default {
        data() {
          return {
            tabData: [],
          };
        },
        // 组件实例配置完成,但是DOM尚未渲染,进行网络请求,配置响应数据
        created() {
          this.loadHotTabs();
        },
        methods: {
          // 利用 mapMutations函数,可以直接把vuex中的mutation合并到当前组件的methods中,合并之后,
          // 使用mutation就像使用methods中的方法一样
          ...mapMutations('search', ['removeSearchData', 'removeAllSearchData']),
      
          // 删除所有数据
          onDelAllClick() {
            this.removeAllSearchData();
          },
          //  删除指定数据 index角标
          onClearAll(item, index) {
            this.removeSearchData(index);
          },
          /**
           * 获取热搜标题数据
           */
          async loadHotTabs() {
            // uniapp 支持 async await
            const  res= await getHotTabs();
            this.tabData = res.content;
            console.log('res', res.content);
          },
        },
        computed: {
          // 2. 在 computed 中,通过 mapState 函数,注册 state 中的数据,导入之后的数据可直接使用(就像使用 data 中的数据一样)
          // mapState(模块名, ['字段名','字段名','字段名'])
          ...mapState('search', ['searchData']),
        },
      };
      </script>
      
      <style lang="scss"></style>
      
      
        14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      六、vuex数据持久化

      已完成 数据和组件的分离,所以【数据持久化】不需要涉及到组件内的代码
      用到的api是uni.setStorage,参数key value形式
      案例演示:搜索模块

      6.1. 搜索模块数据持久化

      store/search.js

      const STORAGE_KEY = 'search-list';
      const HISTORY_MAX = 10;
      
      export default {
        // 独立命名空间
        namespaced: true,
        // 通过 state 声明数据
        state: () => ({
          // 优先从 storage 中读取
          searchData: uni.getStorageSync(STORAGE_KEY) || []
        }),
        // 更改 state 数据的唯一方式是:提交 mutations
        mutations: {
          /**
           * 保存数据到 storage
           */
          saveToStorage(state) {
            uni.setStorage({
              key: STORAGE_KEY,
              data: state.searchData
            });
          },
          /**
           * 添加数据
           */
          addSearchData(state, val) {
            if (!val) return;
            const index = state.searchData.findIndex((item) => item === val);
            if (index !== -1) {
              state.searchData.splice(index, 1);
            }
            // 判断是否超过了最大缓存数量
            if (state.searchData.length > HISTORY_MAX) {
              state.searchData.splice(HISTORY_MAX - 1, state.searchData.length - HISTORY_MAX - 1);
            }
      
            state.searchData.unshift(val);
            // 调用 saveToStorage
            this.commit('search/saveToStorage');
          },
          /**
           * 删除指定数据
           */
          removeSearchData(state, index) {
            state.searchData.splice(index, 1);
            // 调用 saveToStorage
            this.commit('search/saveToStorage');
          },
          /**
           * 删除所有数据
           */
          removeAllSearchData(state) {
            state.searchData = [];
            // 调用 saveToStorage
            this.commit('search/saveToStorage');
          }
        }
      };
      
      
        14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      6.2. 搜索api

      搜索结果 - 获取搜索结果数据

      import request from '../utils/request';
      
      /**
       * 搜索结果
       */
      export function getSearchResult(data) {
        return request({
          url: '/search',
          data
        });
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      6.3. 页面使用

      search-result-list.vue

      <template>
        <view> 搜索结果 </view>
      </template>
      
      <script>
      import { getSearchResult } from 'api/search';
      export default {
        name: 'search-result-list',
        props: {
          // 搜索关键字
          queryStr: {
            type: String,
            required: true
          }
        },
        data() {
          return {
            // 数据源
            resultList: [],
            // 页数
            page: 1
          };
        },
        created() {
          this.loadSearchResult();
        },
        methods: {
          /**
           * 获取搜索数据
           */
          async loadSearchResult() {
            const { data: res } = await getSearchResult({
              q: this.queryStr,
              p: this.page
            });
            this.resultList = res.list;
            console.log(this.resultList);
          }
        }
      };
      </script>
      
      <style lang="scss"></style>
      
        14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43

      相关技术文章

      点击QQ咨询
      开通会员
      返回顶部
      ×
      微信扫码支付
      微信扫码支付
      确定支付下载
      请使用微信描二维码支付
      ×

      提示信息

      ×

      选择支付方式

      • 微信支付
      • 支付宝付款
      确定支付下载