微信小程序使用状态管理 - mobx-miniprogram

发布于:2025-03-22 ⋅ 阅读:(23) ⋅ 点赞:(0)
  • mobx-miniprogram 是针对微信小程序开发的一个简单、高效、轻量级状态管理库,它基于Mobx状态管理框架实现。
  • 使用 mobx-miniprogram 定义管理的状态是响应式的,当状态一旦它改变,所有关联组件都会自动更新相对应的数据
  • 通过该扩展工具库,开发者可以很方便地在小程序中全局共享的状态,并自动更新视图组件,从而提升小程序的开发效率
  • 需要注意:在使用 mobx-miniprogram 需要安装两个包:mobx-miniprogrammobx-miniprogram-bindings
    1. mobx-miniprogram 的作用:创建 Store 对象,用于存储应用的数据
    2. mobx-miniprogram-bindings 的作用:将状态和组件、页面进行绑定关联,从而在组件和页面中操作数据
  1. mobx-miniprogram 官方文档

  2. mobx-miniprogram-bindings 官方文档

一、创建 Store 对象

  • 如果需要创建 Store 对象需要使用 mobx-miniprogram ,因此需要先熟悉 mobx-miniprogram 三个核心概念:
    1. observable:用于创建一个被监测的对象,对象的属性就是应用的状态(state),这些状态会被转换成响应式数据。
    2. action:用于修改状态(state)的方法,需要使用 action 函数显式的声明创建。
    3. computed:根据已有状态(state)生成的新值。计算属性是一个方法,在方法前面必须加上 get 修饰符
  1. 在项目的根目录下,使用如下命令,将快速在根目录下初始化生成一个 package.json 文件

    npm init -y
    
  2. 安装所需的依赖

     npm install mobx-miniprogram mobx-miniprogram-bindings
    
  3. 然后 在 微信开发者工具 的左上角 点击》工具》 构建 npm,构建成功后,将会在项目根目录下生成 miniprogram_npm 文件夹,可以在 miniprogram_npm 文件夹中看见构建的结果

  4. 在项目的根目录下创建 stores 文件夹,然后在该文件夹下新建 numStore.js 文件

  5. /stores/numStore.js 导入 observable action 方法。使用 observable 方法需要接受一个 store 对象,存储应用的状态

    import {
      observable,
      action
    } from 'mobx-miniprogram'
    
    export const numStore = observable({
      numA: 1,
      numB: 2,
      // 使用 action 更新 numA 以及 numB
      // action 中不能使用箭头函数,会找不到 this
      update: action(function () {
        this.numA += 1
        this.numB += 1
      }),
    
      // 计算属性,使用 get 修饰符,
      get sum() {
        return this.numA + this.numB;
      }
    })
    

二、在组件中使用 store 中的数据和方法

  • 如果需要在 Page(页面) 或者Component(组件)中对共享的数据进行读取、更新操作,需要使用 mobx-miniprogram-bindings

  • mobx-miniprogram-bindings 的作用就是将 Store 和 页面或组件进行绑定关联

  • 如果需要在组件中使用状态,需要 mobx-miniprogram-bindings 库中导入 ComponentWithStore 方法,在使用时:需要将 Component(构建组件时的函数) 方法替换成 ComponentWithStore 方法,原本组件配置项也需要写到该方法中。在替换以后,就会新增一个 storeBindings 配置项,配置项常用的属性有以下三个:

    1. store: 指定要绑定的 Store 对象
    2. fields: 指定需要绑定的 data 字段
    3. actions: 指定需要映射的 actions 方法

    注意事项:

    导入的数据会同步到组件的 data 中

    导入的方法会同步到组件的 methods 中

  1. 在项目的根目录下的 components 文件夹中(没有该文件夹的需要自己创建)新建 custom01 文件夹,并在该文件夹中创建 custom01组件(在文件夹上点击鼠标右键,选择 新建 component

  2. 找到项目根目录下的 app.json 文件,增加如下代码,将 custom01组件注册为 全局组件

    {
      // ...其他配置项
      
      "usingComponents": {
        "custom01": "./components/custom01/custom01"
      }
    }
    
  3. pages/index.wxml 中使用 custom01 组件

    <custom01 />
    
  4. 修改components/custom01/custom01.js 文件, Component 方法替换成 ComponentWithStore 方法

    import {
      ComponentWithStore
    } from 'mobx-miniprogram-bindings'
    
    import {
      numStore
    } from '../../stores/numStore'
    
    ComponentWithStore({
      data: {},
      methods: {},
      // 用来配置当前组件需要与哪些store 进行关联
      // fields 将被注入到 组件的 data 属性中
      // actions 将被注入到 组件的 methods 属性中
      storeBindings: {
        store: numStore,
        // 需要使用 store 中的哪些数据
        fields: ['numA', 'numB', 'sum'],
        // 需要使用 store 中的哪些方法
        actions: ['update']
      }
    })
    
  5. 修改components/custom01/custom01.wxml 文件

    <view>{{numA}} + {{numB}} = {{sum}}</view>
    <button type="primary" bind:tap="update">更新store 中的数据</button>
    

三、在页面中使用 store 中的数据和方法

  • 如果需要在 Page(页面) 或者Component(组件)中对共享的数据进行读取、更新操作,需要使用 mobx-miniprogram-bindings
  • mobx-miniprogram-bindings 的作用就是将 Store 和 页面或组件进行绑定关联

(一) 方式一:将页面当成组件

  • Component 方法用于创建自定义组件。小程序的页面也可以视为自定义组件,因此页面也可以使用 Component 方法进行构建,从而实现复杂的页面逻辑开发。
  • 如果我们使用了 Component 方法来构建页面,那么页面中如果想使用 Store 中的数据,使用方式和组件的使用方式是一样的
    1. mobx-miniprogram-bindings 库中导入 ComponentWithStore 方法
    2. Component 方法替换成 ComponentWithStore 方法
    3. 然后配置 storeBindingsStore 中映射数据和方法即可
  1. 假设有一个页面 pages/cate ,修改 pages/cate/cate.js 文件

    // 将页面当成组件
    import {
      ComponentWithStore
    } from "mobx-miniprogram-bindings"
    
    import {
      numStore
    } from '../../stores/numStore'
    
    ComponentWithStore({
      data: {
        msg: '我是cate 页面'
      },
      // 用来配置当前组件需要与哪些store 进行关联
      // fields 将被注入到 组件的 data 属性中
      // actions 将被注入到 组件的 methods 属性中
      storeBindings: {
        store: numStore,
        // 需要使用 store 中的哪些数据
        fields: ['numA', 'numB', 'sum'],
        // 需要使用 store 中的哪些方法
        actions: ['update']
      }
    })
    
  2. 修改 pages/cate/cate.wxml 文件

    <view>{{numA}} + {{numB}} = {{sum}}</view>
    <button type="primary" bind:tap="update">更新store 中的数据</button>
    

(一) 方式二:BehaviorWithStore 方法

  • 如果不想使用 Component 方法构建页面。这时候需要使用 mobx-miniprogram-bindings 提供的 BehaviorWithStore 方法来和 Store 建立关联。
  • 小程序的 behavior 方法是一种代码复用的方式,可以将一些通用的逻辑和方法提取出来,然后在多个组件中复用,从而减少代码冗余,提高代码的可维护性。在页面中也可以使用 behaviors 配置项
  1. 假设有一个页面 pages/category ,在 pages/category 中新建 behavior.js 文件,从 mobx-miniprogram-bindings 库中导入 BehaviorWithStore 方法

    import {
      BehaviorWithStore
    } from 'mobx-miniprogram-bindings'
    import {
      numStore
    } from '../../stores/numStore'
    
    
    export const categoryBehavior = BehaviorWithStore({
      storeBindings: {
        store: numStore,
        // 需要使用 store 中的哪些数据
        fields: ['numA', 'numB', 'sum'],
          // 需要使用 store 中的哪些方法
        actions: ['update']
      }
    })
    
  2. 修改 pages/category/category.js 页面

    import {
      categoryBehavior
    } from './behavior'
    
    Page({
      // 使用 behaviors 配置项注册提取的 categoryBehavior
      behaviors: [categoryBehavior]
    })
    
  3. 修改 pages/category/category.wxml 页面

    <view>{{numA}} + {{numB}} = {{sum}}</view>
    <button type="primary" bind:tap="update">更新store 中的数据</button>
    

四、fields、actions 对象写法

  • storeBindings 中的 fieldsactions 有两种写法:数组 或者 对象。前面使用的都是数组写法

  • 如果 fields 写成对象方式,有两种写法:

    1. 映射形式:指定 data 中哪些字段来源于 store 以及它们在 store 中对应的名字。

      • 例如 { a: 'numA', b: 'numB' }
    2. 函数形式:指定 data 中每个字段的计算方法

      • 例如 { a: () => store.numA, b: () => anotherStore.numB }
  • 如果 actions 写成对象方式,只有一种写法:

    1. 映射形式:指定模板中调用的哪些方法来源于 store 以及它们在 store 中对应的名字。
      • 例如 { buttonTap: 'update' }
  1. 对前面的 组件中使用store 中的的数据和方法的用例进行(components/custom01/custom01.js)修改

    import {
      ComponentWithStore
    } from 'mobx-miniprogram-bindings'
    
    import {
      numStore
    } from '../../stores/numStore'
    
    ComponentWithStore({
      data: {},
      methods: {},
      // 用来配置当前组件需要与哪些store 进行关联
      // fields 将被注入到 组件的 data 属性中
      // actions 将被注入到 组件的 methods 属性中
      storeBindings: {
        store: numStore,
        // 需要使用 store 中的哪些数据
        // fields: ['numA', 'numB', 'sum'],
        // actions: ['update']
    
        fields: {
          // 映射形式:需要指定data 中的哪些字段来源于 store, 以及在 store 中的名字是什么
          // numA: 'numB',
          // numB: 'numB',
          // sum: 'sum'
    
          // 函数形式:
          // key: data 中的哪些字段来源于 store
          // value: 函数。函数内部需要返回对应 store 数据的值
          // numA: ()=> numStore.numA,
          // numB: ()=> numStore.numB,
          // sum: ()=> numStore.sum
    
          // 自定义属性:模板中需要使用自定义后的属性,即 .wxml 页面中要用 a 来代替 numA
          a: 'numA',
          b: 'numB',
          total: 'sum'
        },
        actions: {
          //只有映射形式
          // update: 'update'
    
          // 可以改名
          updateData: 'update',
        }
      }
    })
    

五、绑定多个 store 以及命名空间

  • 在实际开发中,一个页面或者组件可能会绑定多个 Store ,这时候我们可以将 storeBindings 改造成数组。数组每一项就是一个个要绑定的 Store
  • 如果多个 Store 中存在相同的数据,显示会出现异常。还可以通过 namespace 属性给当前 Store 开启命名空间,在开启命名空间以后,访问数据的时候,需要加上 namespace 的名字才可以。
  1. 因为前面的用例中已经创建了 stores/numStore.js 文件,这里不再重复

  2. stores 文件夹中创建 cloneStore.js 文件

    import {
      observable,
      action
    } from 'mobx-miniprogram'
    
    export const cloneStore= observable({
      numA: 10,
      numB: 20,
      // 使用 action 更新 numA 以及 numB
      update: action(function () {
        this.numA += 1
        this.numB += 1
      }),
    
      // 计算属性,使用 get 修饰符,
      get sum() {
        return this.numA + this.numB;
      }
    })
    
  3. 假设 pages/category.js 需要使用两个 store,

    // 将页面当成组件
    import {
      ComponentWithStore
    } from "mobx-miniprogram-bindings"
    
    import {
      numStore
    } from '../../stores/numStore'
    
    import {
      cloneStore
    } from '../../stores/cloneStore'
    
    
    ComponentWithStore({
      // 使用多个 store 需要将storeBindings 改为数组
      // 如果 多个 store 引入时,中存在相同的数据和方法,就会报错
      // 同名时,解决方案一:fields、actions使用对象方式重命名
      // storeBindings: [{
      //   store: numStore,
      //   fields: {
      //     a: 'numA',
      //     b: 'numB',
      //     total: 'sum',
      //   },
      //   actions: {
      //     numUpdate: 'update'
      //   }
      // }, {
      //   store: cloneStore,
      //   fields: ['numA', 'numB', 'sum'],
      //   actions: {
      //     cloneUpdate: 'update'
      //   }
      // }]
    
      // 同名时,解决方案二:添加命名空间
      // 命名空间 只能解决 fields 存在的冲突,actions冲突无法解决
      // actions冲突 依然需要对象方法
      // .wxml 中需要使用 命名空间.numA  来访问数据
      storeBindings: [{
        namespace: 'numStore',
        store: numStore,
        fields: ['numA', 'numB', 'sum'],
        actions: ['update']
      }, {
        namespace: 'cloneStore',
        store: cloneStore,
        fields: ['numA', 'numB', 'sum'],
        actions: {
          cloneUpdate: 'update'
        }
      }]
    })