一、mobx-miniprogram 介绍
随着项目的业务逻辑越来越复杂,组件和页面间通信就会变得非常复杂。例如:有些状态需要在多个页面间进行同步使用,一个地方发生变更,所有使用的地方都需要发生改变,这时候如果使用前面的数据通信方案进行传递数据,给管理和维护将存在很大的问题。
为了方便进行页面、组件之间数据的传递,小程序官方提供了一个扩展工具库:mobx-miniprogram
mobx-miniprogram 时针对小程序开发的一个简单、高效、轻量级状态管理库,它基于 Mobx 状态管理框架实现。
使用 mobx-miniprogram 定义管理的状态时响应式的,当状态一旦它改变,所有关联组件都会自动更新相对应的数据
通过该扩展工具库,开发者可以很方便地在小程序中全局共享地状态,并自动更新视图组件,从而提升小程序地开发效率
二、注意
在使用 mobx-miniprogram 需要安装两个包:mox-miniprogram 和 mobx-miniprogram-bindings
- mobx-miniprogram 地作用:创建 Store 对象,用于存储应用地数据
- mobx-miniprogram-bindings 的作用:将状态和组件、页面进行绑定关联,从而在组件和页面中操作数据
npm install mobx-miniprogram mobx-miniprogram-bindings
三、官方文档
mobx-miniprogram 官方文档
mobx-miniprogram-bindings 官方文档
四、创建Store 对象、使用 Store数据
如果需要创建Store对象需要使用mobx-miniprogram,因此需要先熟悉mobx-miniprogram三个核心概念:
- observable:用于创建一个被检测的对象,对象的属性就是应用的状态(state),这些状态会被转换成响应式数据。
- action:用于修改状态(state)的方法,需要使用 action 函数显示的声明创建。
- computed:根据已有状态(state)生成的新值.计算属性是一个方法,在方法前面必须加上get修饰符。
mobx-miniprogram 详细的使用步骤如下:
- 在项目的根目录下创建 store 文件夹,然后在该文件夹下新建 numStore.js
- 在 /store/index.js 导入 observable、action 方法
import {observable,action} from 'mobx-miniprogram'
- 使用 observable 方法需要接受一个 store 对象,存储应用的状态
import { observable, action } from 'mobx-miniprogram'
export const numStore = observable({
num1: 1,
num2: 2,
// 定义 action 方法,用来修改状态
update: action(function() {
// 在方法中如果需要获取状态,可以使用this进行获取
this.num1 += 1;
this.num2 += 2;
}),
// 计算数学 computed
// 是根据已有的状态,产生新的状态
// 计算属性前面需要使用 get 修饰符进行修饰
get sum() {
return this.num1 + this.num2
}
})
- 在组件中使用数据
如果需要 Page 或 Component 中对共享的数据进行读取、更新操作,需要使用 mobx-miniprogram-bindings
mobx-miniprogram-bindings 的作用就是将 Store 和页面或组件进行绑定关联
如果需要在组件中使用状态,需要 mobx-miniprogram-bindings 库中导入 ComponentWithStore 方法
使用时:需要将 Component 方法 替换成 ComponentWithStore 方法,原本组件配置也需要写到该方法中。
在替换以后,就会新增一个 storeBindings 配置项,配置项常用的属性有以下三个:
- store:指定要绑定 Store 对象
- fields:指定需要绑定的 data 字段
- actions:指定需要映射的 actions 方法
注意事项:
导入的数据会同步到组件的 data 中
导入的方法会同步到组件的 methods 中
示例:
1.新建全局组件 custom01
2. 在 custom01.js 文件中
// 如果需要在组件中使用 Store 中的数据以及方法
// 需要从 mobx-miniprogram-bindings 里面引入
import { ComponentWithStore } from 'mobx-miniprogram-bindings'
// 导入当前组件需要使用的 Store 对象
import { numStore } from '../../stores/numStore'
// 需要使用 ComponentWithStore 方法将 Component 方法进行替换
ComponentWithStore({
// 用来配置当前组件需要与那些 Store 进行关联
storeBindings: {
store: numStore,
fields: ['num1', 'num2', 'sum'],
actions: ['update']
}
})
注意: 在从Store 对象中引入数据和方法以后 如果是数据,会被注入到 data 对象中 如果是方法,会被注入到 methods 对象中
3.打开 custom01.wxml中
<view>{{num1}}+{{num2}}={{sum}}</view>
<button type="warn" plain bindtap="update">更新 Store 中的数据</button>
4.页面中使用 Store 数据方式
第一种方式:
Component 方法用于创建自定义组件。
小程序的页面也可以视为自定义组件,因此页面也可以使用 Component 方法进行构建,从而实现复杂的页面逻辑开发。
如果我们使用了Component 方法来构建页面,那么页面中如果想使用 Store 中的数据,使用方式和组件的使用方式是一样的。
- 从 mobx-miniprogram-bindings 库中导入 ComponetWithStore 方法
- 将 Component 方法 替换成 ComponentWithStore 方法
- 然后配置 storeBindings 从 Store 中映射数据和方法即可
打开 cart.js 把 Page 改成 ComponentWithStore
// 如果需要在组件中使用 Store 中的数据以及方法
// 需要从 mobx-miniprogram-bindings 里面引入
import { ComponentWithStore } from 'mobx-miniprogram-bindings'
// 导入当前组件需要使用的 Store 对象
import { numStore } from '../../stores/numStore'
// 需要使用 ComponentWithStore 方法将 Component 方法进行替换
ComponentWithStore({
// 用来配置当前组件需要与那些 Store 进行关联
storeBindings: {
store: numStore,
fields: ['num1', 'num2', 'sum'],
actions: ['update']
}
})
打开 cart.wxml 文件 直接使用
<view>{{num1}}+{{num2}}={{sum}}</view>
<button type="warn" plain bindtap="update">更新 Store 中的数据</button>
第二种方式:
需要使用 mobx-miniprogram-bindings 提供的 BehaviorWithStore 方法来和 Store 建立关联。
小程序的 behavior 方法是一种代码复用的方式,可以将一些通用的逻辑和方法提取出来,然后在多个组件中复用,从而减少代码冗余,提高代码的可维护性,在页面中也可以使用 behaviors 配置项
使用方式如下:
- 新建 behavior 文件,从 mobx-miniprogram-bindings 库中导入 BehaviorWithStore 方法
- 在 BehaviorWithStore 方法中配置 storeBindings 配置项从 Store 中映射数据和方法
- 在 Page 方法中导入创建的 behavior ,然后配置 behavior 属性,并使用导入的 behavior
在cart 文件夹下新建文件 【behaviors.js】
// 小程序页面如果想使用 Store 对象中的数据或方法
import { BehaviorWithStore } from 'mobx-miniprogram-bindings'
import { numStore } from '../../stores/numStore'
// BehaviorWithStore 方法的作用:
// 让页面和Stroe 对象建立关联
export const cartBehavior = BehaviorWithStore({
storeBindings: {
store: numStore,
fields: ["num1", "num2", "sum"],
actions: ["update"]
}
})
打开 cart.js 文件
// 需要先导入 提取的
import { cartBehavior } from './behaviors'
Page({
// 使用 behaviors 配置项 提取的 behavior
behaviors: [cartBehavior]
})
打开 cart.wxml 文件
<view>{{num1}}+{{num2}}={{sum}}</view>
<button type="warn" plain bindtap="update">更新 Store 中的数据</button>
五、fields、actions 对象写法
filds 和 actions 有两种写法:数组写法 和 对象写法 首推 数组写法
数组写法
storeBindings: {
store: numStore,
fields: ['num1', 'num2', 'sum'],
actions: ['update']
}
对象写法
fields 数据有两种写法 :映射形式和函数形式
映射形式:需要指定 data 中哪些字段 来源于 store ,以及在 store 中的名字是什么
fields: {
num1: 'num1',
num2: 'num2',
sum: 'sum'
},
函数形式:key: data 中哪些字段 来源于 store ;value: 函数,函数内部需要返回对应 store 数据的值
fields: {
num1: () => numStore.num1,
num2: () => numStore.num2,
sum: () => numStore.sum,
},
如果将 actions 改为对象写法 actions 只有一种写法
指定模板中使用的哪些方法来源于 store,并且 store 中的名字是什么
actions: {
update: 'update'
}
六、绑定多个 store 以及命名空间
如果一个组件或者页面需要绑定多个 Store 对象
需要将 storeBindings 配置项改造成一个数组
数组每一项是一个个要绑定的 Store 对象
注意:
如果 一个组件或页面需要绑定多个 Store 对象
从 Store 对象中引入了相同的数据或者方法
这时候代码就会出现异常
解决办法:
- 第一种解决方案:将其中一个 store 的fields 以及actions 改成对象方式
- 第二种解决方案:添加命名空间
如果是数据存在冲突(存在相同的数据或方法),添加命名空间没有问题
但是如果是方法冲突(存在相同的数据或方法),依然需要使用对象的方式来改造
在添加命名空间以后,如果需要访问数据,需要加上空间的名字才可以
// 如果需要在组件中使用 Store 中的数据以及方法
// 需要从 mobx-miniprogram-bindings 里面引入
import { ComponentWithStore } from 'mobx-miniprogram-bindings'
// 导入当前组件需要使用的 Store 对象
import { numStore } from '../../stores/numStore'
import { cloneStore } from "../../stores/cloneStore"
// 需要使用 ComponentWithStore 方法将 Component 方法进行替换
ComponentWithStore({
storeBindings: [{
store: numStore,
fields: ['num1', 'num2', 'sum'],
actions: ['update']
},
{
namespace: "cloneStore",
store: cloneStore,
fields: ['num1', 'num2'],
actions: {
update1: "update"
}
},
]
})
在添加命名空间以后,如果需要访问数据,需要加上命名空间的名字才可以
<view>{{cloneStore.num1}}+{{cloneStore.num2}}={{cloneStore.sum}}</view>
<button type="warn" plain bindtap="update1">更新 Store 中的数据</button>
七、miniprogram-computed
小程序框架没有提供计算属性相关的 api ,但是官方为开发者提供了扩展工具库 miniprogram-computed
该工具库提供了两个功能:
1.计算属性 computed
2.监听器 watch
npm install --save miniprogram-computed
计算属性:基于已有的数据产生新的数据
在使用 ComponentWidthComputed 方法构建组件以后
这时候,就可以新增两个配置项 computed 以及 watch 配置项
计算属性 computed
计算属性方法内部必须有返回值
在计算属性内部,不能使用this来获取 data 中的数据
如果想获取 data 中的数据,需要使用 形参
只要计算属性依赖的数据发生了变化,计算属性就会重新进行计算
// 需要使用导入的ComponentWithComputed 替换 Compoent 方法
import { ComponentWithComputed } from 'miniprogram-computed'
ComponentWithComputed({
computed: {
total(data) {
return data.a + data.b
}
},
data: {
a: 1,
b: 2
}
})
计算属性 具有缓存特性
计算属性只执行一次,后续在使用的时候,返回的是第一次执行的结果
只要依赖的数据,没看发生改变,返回的始终是第一次执行的结果
监听器 watch
watch 数据监听器,用来监听数据是否发生了变化,在数据变化以后执行响应的逻辑
key: 需要监听的数据
value: 是回调函数,回调函数有个形参,形参是最新的、改变以后的数据
// 需要使用导入的ComponentWithComputed 替换 Compoent 方法
// 需要使用导入的ComponentWithComputed 替换 Compoent 方法
import { ComponentWithComputed } from 'miniprogram-computed'
ComponentWithComputed({
computed: {
total(data) {
return data.a + data.b
}
},
watch: {
a: function(a) {
console.log(a)
},
b: function(b) {
console.log(b)
}
},
data: {
a: 1,
b: 2
},
methods: {
updateData() {
this.setData({
a: this.data.a + 1,
b: this.data.b + 2
})
}
}
})
watch简写方式
同时监听多个数据,数据与数据之间需要使用 ,进行分隔
在watch 内部监听到数据变化以后,就可以进行响应的逻辑
watch: {
'a,b': function(a, b) {
console.log(a, b)
}
},
八、Mobx 与 Computed 结合使用
两个框架扩展提供的 ComponentWithStore 与 ComponentWithComputed 方法无法结合使用。
如果需要在一个组件中既想使用 mobx-miniprogram-bindings 又想使用 miniprogram-computed
解决方案是:
- 使用旧版 API
- 自定义组件仍然使用 Component 方法构建组件,将两个扩展依赖包的使用全部改为旧版 API
- 即添加 computedBehavior 或 storeBindingsBehavior
- mobx-miniprogram-bindings
- miniprogram-computed
- 使用兼容写法(推荐使用此方法)
- 即要么使用 ComponentWithStore 方法构建组件,要么使用 ComponentWithComputed 方法构建组件
- 如果使用了 ComponentWithStore 方法构建组件,计算属性写法使用旧版 API
- 如果使用了 ComponentWithComputed 方法构建组件,Mobx 写法使用旧版 API
目前组件使用的是 ComponentWithComputed 进行构建
如果想和 stroe 对象建立关联、进行绑定
Mobx 需要使用旧版 API
// 从 mobx-miniprogram-bindings 里面引入 ComponentWithStore 方法
import { ComponentWithStore } from 'mobx-miniprogram-bindings'
// 导入当前组件需要使用的 Store 对象
import { numStore } from '../../stores/numStore'
// 如果使用 ComponentWidthStore 方法构建组件
// 计算属性扩展库需要使用旧版 API
// 导入计算属性 behavior
const computedBehavior = require('miniprogram-computed').behavior
// 注册 behavior
ComponentWithStore({
// 注册 behavior
behaviors: [computedBehavior],
computed: {
total(data) {
return data.a + data.b
}
},
watch: {
'a,b': function(a, b) {
console.log(a, b)
}
},
data: { a: 1, b: 2 },
methods: {
updateData() {
this.setData({
a: this.data.a + 1,
b: this.data.b + 1
})
}
},
storeBindings: {
store: numStore,
fields: ['num1', 'num2', 'sum'],
actions: ['update']
}
})
<view>{{num1}} + {{num2}} = {{sum}} </view>
<button type="warn" plain bindtap="update">更新 Store 中的数据</button>
<view>{{a}}+{{b}}={{total}}</view>
<button type="primary" bindtap="updateData">更新数据</button>
反之:
import { ComponentWithComputed } from 'miniprogram-computed'
import { numStore } from '../../stores/numStore'
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings'
ComponentWithComputed({
behaviors: [storeBindingsBehavior],
storeBindings: {
store: numStore,
fields: ['num1', 'num2', 'sum'],
actions: ['update']
},
computed: {
total(data) {
return data.a + data.b
}
},
methods: {
updateData() {
this.setData({
a: this.data.a + 1,
b: this.data.b + 1
})
}
},
data: {
a: 1,
b: 2
}
})