Skip to content

wechat-miniprogram/mobx-miniprogram-bindings

Repository files navigation

小程序的 MobX 绑定辅助库

小程序的 MobX 绑定辅助库。

此 behavior 依赖开发者工具的 npm 构建。具体详情可查阅 官方 npm 文档

使用方法

需要小程序基础库版本 >= 2.11.0 的环境。

具体的示例完整代码,可以参考 examples

  1. 安装 mobx-miniprogrammobx-miniprogram-bindings
npm install --save mobx-miniprogram mobx-miniprogram-bindings
  1. 创建 MobX Store。
// store.js
import { observable, action } from 'mobx-miniprogram'

// 创建 store 时可以采用任何 mobx 的接口风格
// 这里以传统的 observable 风格为例

export const store = observable({
  // 数据字段
  numA: 1,
  numB: 2,

  // 计算属性
  get sum() {
    return this.numA + this.numB
  },

  // actions
  update: action(function () {
    const sum = this.sum
    this.numA = this.numB
    this.numB = sum
  }),
})
  1. 在 Component 构造器中使用:
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
import { store } from './store'

Component({
  behaviors: [storeBindingsBehavior], // 添加这个 behavior
  data: {
    someData: '...',
  },
  storeBindings: {
    store,
    fields: {
      numA: () => store.numA,
      numB: (store) => store.numB,
      sum: 'sum',
    },
    actions: {
      buttonTap: 'update',
    },
  },
  methods: {
    myMethod() {
      this.data.sum // 来自于 MobX store 的字段
    },
  },
})

TypeScript 接口

在 TypeScript 下,可以使用 ComponentWithStore 接口。它会自动处理一些类型问题。注意:

  • 使用这个接口时,不要在 behaviors 中额外引入 storeBindingsBehavior
  • fieldsactions 末尾需要加上 as const 以便更好的类型推导;
  • storeBindings 如果是一个数组,也要在数组后加上 as const
import { ComponentWithStore } from 'mobx-miniprogram-bindings'

ComponentWithStore({
  data: {
    someData: '...',
  },
  storeBindings: {
    store,
    fields: ['numA', 'numB', 'sum'] as const,
    actions: {
      buttonTap: 'update',
    } as const,
  },
})

BehaviorWithStore 接口类似。

import { BehaviorWithStore } from 'mobx-miniprogram-bindings'

export const testBehavior = BehaviorWithStore({
  storeBindings: {
    store,
    fields: ['numA', 'numB', 'sum'] as const,
    actions: ['update'] as const,
  },
})

目前 TypeScript 接口定义依赖于 miniprogram-api-typings ^4.0.0 。 (如使用老版本的 api-typings ,请使用本项目的 v4 或 v3 版本。)

glass-easel Chaining API 接口

使用 glass-easel Chaining API 时,使用 initStoreBindings 更友好。

import { initStoreBindings } from 'mobx-miniprogram-bindings'

Component()
  .init((ctx) => {
    const { listener } = ctx
    initStoreBindings(ctx, {
      store,
      fields: ['numA', 'numB', 'sum'],
    })
    const buttonTap = listener(() => {
      store.update()
    })
    return { buttonTap }
  })
  .register()

具体接口说明

将页面、自定义组件和 store 绑定有两种方式: behavior 绑定手工绑定

behavior 绑定

behavior 绑定 适用于 Component 构造器。做法:使用 storeBindingsBehavior 这个 behavior 和 storeBindings 定义段。

import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'

Component({
  behaviors: [storeBindingsBehavior],
  storeBindings: {
    /* 绑定配置(见下文) */
  },
})

也可以把 storeBindings 设置为一个数组,这样可以同时绑定多个 store

import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'

Component({
  behaviors: [storeBindingsBehavior],
  storeBindings: [
    {
      /* 绑定配置 1 */
    },
    {
      /* 绑定配置 2 */
    },
  ],
})

手工绑定

手工绑定 更加灵活,适用于 store 需要在 onLoad (自定义组件 attached )时才能确定的情况。目前,在 Page 构造器内使用时,也必须用这种方式。

做法:使用 createStoreBindings 创建绑定,它会返回一个包含清理函数的对象用于取消绑定。

注意:在页面 onUnload (自定义组件 detached )时一定要调用清理函数,否则将导致内存泄漏!

import { createStoreBindings } from 'mobx-miniprogram-bindings'

Page({
  onLoad() {
    this.storeBindings = createStoreBindings(this, {
      /* 绑定配置(见下文) */
    })
  },
  onUnload() {
    this.storeBindings.destroyStoreBindings()
  },
})

绑定配置

无论使用哪种绑定方式,都必须提供一个绑定配置对象。这个对象包含的字段如下:

字段名 类型 含义
store 一个 MobX observable 默认的 MobX store
fields 数组或者对象 用于指定需要绑定的 data 字段
actions 数组或者对象 用于指定需要映射的 actions

fields

fields 有三种形式:

  • 数组形式:指定 data 中哪些字段来源于 store 。例如 ['numA', 'numB', 'sum']
  • 映射形式:指定 data 中哪些字段来源于 store 以及它们在 store 中对应的名字。例如 { a: 'numA', b: 'numB' } ,此时 this.data.a === store.numA this.data.b === store.numB
  • 函数形式:指定 data 中每个字段的计算方法。例如 { a: () => store.numA, b: () => anotherStore.numB } ,此时 this.data.a === store.numA this.data.b === anotherStore.numB

上述三种形式中,映射形式和函数形式可以在一个配置中同时使用。

如果仅使用了函数形式,那么 store 字段可以为空,否则 store 字段必填。

actions

actions 可以用于将 store 中的一些 actions 放入页面或自定义组件的 this 下,来方便触发一些 actions 。有两种形式:

  • 数组形式:例如 ['update'] ,此时 this.update === store.update
  • 映射形式:例如 { buttonTap: 'update' } ,此时 this.buttonTap === store.update

只要 actions 不为空,则 store 字段必填。

注意事项

延迟更新与立刻更新

为了提升性能,在 store 中的字段被更新后,并不会立刻同步更新到 this.data 上,而是等到下个 wx.nextTick 调用时才更新。(这样可以显著减少 setData 的调用次数。)

如果需要立刻更新,可以调用:

  • this.updateStoreBindings() (在 behavior 绑定 中)
  • this.storeBindings.updateStoreBindings() (在 手工绑定 中)

与 miniprogram-computed 一起使用

miniprogram-computed 时,在 behaviors 列表中 computedBehavior 必须在后面:

Component({
  behaviors: [storeBindingsBehavior, computedBehavior],
  /* ... */
})

关于部分更新

如果只是更新对象中的一部分(子字段),是不会引发界面变化的!例如:

Component({
  behaviors: [storeBindingsBehavior],
  storeBindings: {
    store,
    fields: ['someObject'],
  },
})

如果尝试在 store 中:

this.someObject.someField = 'xxx'

这样是不会触发界面更新的。请考虑改成:

this.someObject = Object.assign({}, this.someObject, { someField: 'xxx' })