基于Proxy的小程序状态管理实现


Posted in Javascript onJune 14, 2019

微信小程序的市场在进一步的扩大,而背后的技术社区仍在摸索着最好的实践方案。我在帮助Nike,沃尔玛以及一些创业公司开发小程序后,依旧认为使用小程序原生框架是一个更高效,稳定的选择,而使用原生框架唯独缺少一个好的状态管理库,如果不引入状态管理则会让我们在模块化,项目结构以及单元测试上都有些捉襟见肘。

目前相对比较稳健的做法是针对redux或者mobx做一个adaptor应用到小程序中,但这样需要自己想办法打包引入外部库,还要想怎么去写这个adaptor,总显得有些麻烦。于是我迸发出一个想法去写一个专用于小程序的状态管理库,它使用起来足够简单并且可以通过小程序自己的npm机制安装。

目前我已经用这个开源库开发了两个电商小程序,在提高我开发效率的同时亦保证了程序的性能,所以接下来我想谈谈这背后的理念以启发更多开发者尝试新的解决方案。

基于Proxy的状态管理实现

Proxy在小程序中已经得到了足够好的支持,目前并没有发现在任何iPhone或者Android上不能使用Proxy的情况。而基于Proxy的状态管理其实也就是订阅监听的模式,一方面监听数据的变化,另一方面将这些变化传达给订阅的小程序页面。

举一个比较常见的例子,当一个用户从自己的主页进入用户编辑页面,然后更改了自己的用户名点击保存后,用户主页和用户编辑页上的用户名这时候都应该被更新。这背后的程序逻辑则是:更新这个行为将触发Proxy去通知状态管理库,然后状态管理库负责检查此时还在页面栈中的所有页面,更新订阅了用户名这个数据的页面,如下图:

基于Proxy的小程序状态管理实现

Part1: 监听数据变化

监听数据变化其实就是监听各个Store的属性变化,实现上就是在各个Store前面加了一层Proxy,用更直观的图片来表示就是这样:

基于Proxy的小程序状态管理实现

当一个Store被观察以后,它的属性就都变成了Proxy实例,当这个属性值是Object或者Array的时候,它内部的值也会被包装成Proxy实例,这样无论多深层的数据变动都能被监听到。
而在Proxy的后面,Store的属性其实是被另一套数据(紫色部分)所维护,这套数据不负责监听,它就是纯数据,针对属性的任何变动最后都会应用到这套数据上来,它的作用是维护和返回最新的数据。

实现细节: https://github.com/wwayne/minii/blob/master/src/api/observe.js

Part2: 页面数据绑定

因为小程序每个页面的js都是向Page中传递一个对象,这就让我们有机会包装这个对象,从而实现:

进入页面后,将页面保存在页面栈中将来自状态管理库的数据映射到这个页面的data上来页面退出时,将页面从页面栈中移除

实现细节: https://github.com/wwayne/minii/blob/master/src/api/mapToData.js

Part3: 页面订阅更新

当数据被监听到变化后,我们需要依次做两件事,先是找到所有存储在页面栈里的页面,然后根据各个页面订阅的数据来检查变化,如果有变化就通知这些页面,从而让它们去触发setData更新页面。

实现细节:https://github.com/wwayne/minii/blob/master/src/core.js

使用状态管理的例子

有了状态管理库,现在我们就来实现一开始举例的更新用户信息的操作,我们的文件路径如下:

stores/
 user.js
pages/
 userEdit/
   index.js
   index.wxml

1.首先我们创建一个Store保存用户的信息,并且监听它的变化:

// stores/user.js
import { observe } from 'minii'

Class UserStore {
 constructor () {
   this.name = 'bob'
 }

 changeName (name) {
   this.name = name
 }
}

export default observe(new UserStore(), 'user')

2.接着在我们的小程序页面订阅Store的信息

// pages/userEdit/index.js
import { mapToData } from 'minii'
import userStore from '../../stores/user'

const connect = mapToData(state => (({
 myName: state.user.name
}))
Page(connect({
 updateNameToJames () {
  userStore. changeName('james')
 }
}))

3.完成,现在可以在页面中使用和更新数据了

// pages/userEdit/index.wxml
<text>{{ myName }}</text>
<button bindtap="updateNameToJames">update name to James</button>

最后

小程序因为有体积的限制,所以我希望在代码量上也尽量做到轻量和便捷,所以目前这个状态管理库并没有太多很复杂的功能,在小程序打包后所占用的体积也不到1kb,颇有点够用就好的意思。

我也已经用它开发了两款小程序,在经历了一段时间的用户使用后,我也更有信心说这个方案在小程序中是可行的。如果你有任何想法和建议,都欢迎告诉我。

项目Github: https://github.com/wwayne/minii

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript ( (__ = !$ + $)[+$] + ({} + $)[_/_] +({} + $)[_/_] )
Feb 25 Javascript
ECMAScript 5中的属性描述符详解
Mar 02 Javascript
Vue2.0 从零开始_环境搭建操作步骤
Jun 14 Javascript
JavaScript实现瀑布流图片效果
Jun 30 Javascript
基于casperjs和resemble.js实现一个像素对比服务详解
Jan 10 Javascript
vue 插值 v-once,v-text, v-html详解
Jan 19 Javascript
JS设计模式之命令模式概念与用法分析
Feb 06 Javascript
详解使用Next.js构建服务端渲染应用
Jul 10 Javascript
如何让node运行es6模块文件及其原理详解
Dec 11 Javascript
详解Vue之父子组件传值
Apr 01 Javascript
在vue中阻止浏览器后退的实例
Nov 06 Javascript
Vue前端判断数据对象是否为空的实例
Sep 02 Javascript
深度了解vue.js中hooks的相关知识
Jun 14 #Javascript
Vue 实现前进刷新后退不刷新的效果
Jun 14 #Javascript
在Vue中使用icon 字体图标的方法
Jun 14 #Javascript
移动端底部导航固定配合vue-router实现组件切换功能
Jun 13 #Javascript
后台使用freeMarker和前端使用vue的方法及遇到的问题
Jun 13 #Javascript
vue路由插件之vue-route
Jun 13 #Javascript
在JavaScript中使用严格模式(Strict Mode)
Jun 13 #Javascript
You might like
解析php下载远程图片函数 可伪造来路
2013/06/25 PHP
PHP下获取上个月、下个月、本月的日期(strtotime,date)
2014/02/02 PHP
PHP面向对象教程之自定义类
2014/06/10 PHP
基于PHP+jQuery+MySql实现红蓝(顶踩)投票代码
2015/08/25 PHP
php foreach如何跳出两层循环(详解)
2016/11/05 PHP
PHP实现的字符串匹配算法示例【sunday算法】
2017/12/19 PHP
Yii框架日志记录Logging操作示例
2018/07/12 PHP
JQuery调webservice实现邮箱验证(检测是否可用)
2013/05/21 Javascript
js算法中的排序、数组去重详细概述
2013/10/14 Javascript
javascript实现文本域写入字符时限定字数
2014/02/12 Javascript
jq实现酷炫的鼠标经过图片翻滚效果
2014/03/12 Javascript
js日期插件dateHelp获取本月、三个月、今年的日期
2016/03/07 Javascript
判断输入的字符串是否是日期格式的简单方法
2016/07/11 Javascript
js如何判断是否在iframe中及防止网页被别站用iframe嵌套
2017/01/11 Javascript
BootStrap Select清除选中的状态恢复默认状态
2017/06/20 Javascript
Vue中使用方法、计算属性或观察者的方法实例详解
2018/10/31 Javascript
React学习之受控组件与数据共享实例分析
2020/01/06 Javascript
解决vue 使用setTimeout,离开当前路由setTimeout未销毁的问题
2020/07/21 Javascript
对Python字符串中的换行符和制表符介绍
2018/05/03 Python
Flask web开发处理POST请求实现(登录案例)
2018/07/26 Python
python斐波那契数列的计算方法
2018/09/27 Python
Python 加密与解密小结
2018/12/06 Python
python 输出所有大小写字母的方法
2019/01/02 Python
简单了解python关系(比较)运算符
2019/07/08 Python
Python配置文件处理的方法教程
2019/08/29 Python
使用 PyTorch 实现 MLP 并在 MNIST 数据集上验证方式
2020/01/08 Python
Python urlencode和unquote函数使用实例解析
2020/03/31 Python
Keras 在fit_generator训练方式中加入图像random_crop操作
2020/07/03 Python
利用Python将图片中扭曲矩形的复原
2020/09/07 Python
HTML5的postMessage的使用手册
2018/12/19 HTML / CSS
音乐器材管理制度
2014/01/31 职场文书
工作骂脏话检讨书
2014/10/05 职场文书
致创业的您:这类人不适合餐饮创业
2019/08/19 职场文书
二年级作文之动物作文
2019/11/13 职场文书
golang 如何通过反射创建新对象
2021/04/28 Golang
MySQL 分区表中分区键为什么必须是主键的一部分
2022/03/17 MySQL