Loading...
墨滴

qqf

2021/09/25  阅读:47  主题:WeChat-Format

微信小程序的状态管理

前言

开发微信小程序中管理全局共享的状态,如果全部存储在入口文件 app.js 的 globalData 中,那随着项目的不断迭代,globalData 中的状态会越添加越多,变的混乱和不易于维护。

为了解决微信小程序状态管理的问题,分别研究了 mobx-miniprogram 和 westore 专用于解决微信小程序状态管理的工具。

它们两个各有什么特点以及该如何选择。

本文为个人学习总结,如理解有误,欢迎评论纠正

对比

主要从几个方面来看下 mobx-miniprogram 和 westore 的使用区别:

  • 创建 store
  • 在页面中使用、更新 store
  • 在组件中使用、更新 store
  • 延迟更新与立即更新
  • 将 store 划分模块

mobx-miniprogram

mobx-miniprogram 的功能非常纯粹与简单,就是用于创建一个 store。但将 store 的数据映射到页面或组件时,就需要 mobx-miniprogram-bindings 库,它类似 react-redux,用于连接 store 与页面/组件的桥梁。

安装

yarn add mobx-miniprogram mobx-miniprogram-bindings

在小程序中安装第三方包后,使用微信开发者工具的 "构建 npm" 进行构建后即可导入使用。

创建 store

从 mobx-miniprogram 包中导入 observableaction 两个方法。

  • observable: 用于创建 store 的实例对象
  • action: 用于包裹修改 store 数据的函数

store/store.js

import {
  observable,
  action
from "mobx-miniprogram";

export const store = observable({
  count0,
  // 计算属性
  get double () {
    return this.count * 2
  },
  increase: action(function (num{
    this.count += num
  })
})

在页面中使用、更新 store

要用到 mobx-miniprogram-bindings 用于连接 store 与页面。

从 mobx-miniprogram-bindings 包中导入 createStoreBindings 方法。在页面的 onLoad 生命周期钩子中使用它,把指定 store 中的数据字段和更新函数映射到当前页面,供当前页面使用。

import {
  createStoreBindings
from "mobx-miniprogram-bindings"
import store from "../../store/index"

Page({
  onLoad() {
    this.storeBindings = createStoreBindings(this, {
      store,
      fields: ['count''double'],
      actions: ['increase']
    })
  },
  // count 递增
  increaseHandle() {
    this.increase(1)
  },
  // 页面卸载时,销毁当前页面的 store 绑定
  onUnload() {
    this.storeBindings.destroyStoreBindings();
  }
})

createStoreBindings 方法调用会返回一个包含 updateStoreBindings,destroyStoreBindings 两个函数的对象,并且赋值给了当前页面实例的 storeBindings 属性。

所以,当页面卸载(onUnload)时,调用 destroyStoreBindings 销毁当前页面的 store 绑定,避免造成内存泄露。

updateStoreBindings 的用法在下文的 延迟更新与立即更新 中会提到。

在组件中使用、更新 store

与在页面使用 store 方式不同,在组件中要结合 behaviors(类似 Vue 中的混入)方式。

从 mobx-miniprogram-bindings 包中导入 storeBindingsBehavior 方法,并在组件选项中定义 storeBindings 字段。

import {
  storeBindingsBehavior
from "mobx-miniprogram-bindings"
import store from "../../store/index"

Component({
  behaviors: [storeBindingsBehavior],
  storeBindings: {
    store,
    fields: ['count'],
    actions: ['increase']
  },
  methods: {
    // count 递增
    increaseHandle() {
      this.increase(1)
    }
  }
})

延迟更新与立即更新

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

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

  • this.updateStoreBindings() (在组件中)
  • this.storeBindings.updateStoreBindings() (在页面中)
// count 递增
increaseHandle() {
  this.increase(1)
  this.storeBindings.updateStoreBindings()
  console.log(this.data.count) // 更新后的值
}

store 划分模块

随着项目越大,功能越丰富,项目模块的状态也越多,为了防止在一个 store 中堆积其他模块的状态,可根据功能模块或职责划分多个 store。

比如在 store 目录下划分以下模块:

  • systemStore.js
  • userStore.js
  • cartStore.js
  • orderStore.js

页面或组件中需要使用和更新哪些 store 模块的状态,就导入指定的 store 模块,作为 store 字段传递给 createStoreBindings 或 storeBindingsBehavior 即可。

  createStoreBindings(this, {store: xxx})

  storeBindings: {
    store: xxx,
  }

westore

westore 有两个核心的 API:

  • create(store,option) 创建页面
  • create(option) 创建组件
  • update() 更新页面或组件

页面和组件上声明的 data 的值会被 store 上的同名的值覆盖掉。所以页面和组件默认值是在 store.data 中标记,而不是在组件和页面的 data。

安装

https://github.com/Tencent/westore/tree/revert-76-revert-75-revert-73-master/utils 目录下 create.jsdiff.js 两个文件复制到你的小程序项目的根目录下 utils 文件夹中。

创建 store

在项目根目录下创建 store.js 文件,默认导出一个包含 data 的对象,data 对象中的字段就是用于页面与组件共享的状态。

页面和组件上同样需要声明依赖的 data,这样 westore 会按需局部更新。

store.js

export default {
  data: {
    userInfo: {
      name'zhangsan',
      age25,
      city'QingDao'
    },
    count0
  }
}

在页面中使用、更新 store

使用 create 创建页面只需传入两个参数,store 从根节点注入,所有子组件都能通过 this.store 访问。

import create from "../../utils/create"
import store from "../../store"

create(store, {
  data: {
    countnull
  },
  // 更新 count
  increase() {
    this.store.data.count += 1
    this.update()
  }
})

westore 支持直接赋值 sotre.data 更新全局状态,然后通过 update 进行 Diff 比较后更新。

图片来源于 westore 官方文档

使用 webstore 的 this.update 本质是先 diff,再执行一连串的 setData,所以可以保证传递的数据每次维持在最小。

在组件中使用、更新 store

create 创建页面不同的是,create 创建组件只需传入一个参数,不需要传入 store,因为 store 已经从根节点注入了,直接通过 this.store 访问。

import create from "../../utils/create"

create({
  data: {
    countnull
  },
  methods: {
    increase() {
      this.store.data.count += 1
      this.update()
    }
  }
})

延迟更新与立即更新

先明确一点,小程序的 setData() 是异步执行的吗?

  • setData 在逻辑层的操作是同步的,因此 this.data 中的相关数据会立即更新

  • setData 在视图层的操作时异步的,因此页面渲染可能并不会立即发生

我们现在明确了 setData 在逻辑层操作是同步的,根据上文 update 方法调用的原理图:

this.update -> Diff -> setData()

由此可知,this.update() 更新在逻辑层是同步的。

  increase() {
    this.store.data.count += 1
    this.update()
    // 可立即访问更新后的数据
    console.log(this.data.count)
  }

update 方法会返回一个 promise 对象,当视图层页面渲染完成后调用 resolve()。

this.update()
  .then((data) => {
    console.log('视图已完成数据的更新', data)
  })

store 划分模块

把不同模块的 store 拆分成不同的文件,最后合并到一个 store 文件暴露给 create 方法。

  • user.js
export default {
  userInfo: {
    name'zhangsan',
    age25,
    city'QingDao'
  }
}
  • system.js
export default {
  count0
}
  • store.js
import user from "./user"
import system from "./system"

export default {
  data: {
    user,
    system
  }
}

使用和更新

create(store, {
  data: {
    user: {}
  },
  methods: {
    updateName() {
      this.store.data.user.name = 'lisi'
      this.update()
    }
  }
})

总结

以上是 mobx-miniprogram 和 westorm 两种状态管理的基本使用区别,具体使用哪种状态管理工具最主要看你习惯于哪种编写方式。

更详细的介绍,查阅这两个工具的官方文档

总结上文:

  • mobx-miniprogram、mobx-miniprogram-bindings 结合使用

  • mobx-miniprogram 提供了 observable 创建 store,action 更新 store

  • mobx-miniprogram-bindings 在页面中使用 createStoreBinding 映射 store 的字段,在组件 storeBindingsBehavior 与 behaviors 结合

  • mobx-miniprogram 更新 sotre 是异步的

  • westore 提供了 create() 创建页面和组件

  • westore 可以通过 this.store.data.xxx 赋值的方式修改 store

  • westore 的 update 更新是同步的

参考资料

  • https://www.yuque.com/webboys/pnadia/ntbohh
  • https://github.com/wechat-miniprogram/mobx-miniprogram-bindings
  • https://github.com/qiqingfu/westore/tree/revert-76-revert-75-revert-73-master

qqf

2021/09/25  阅读:47  主题:WeChat-Format

作者介绍

qqf