用 RxJS、RxWX 编写微信小程序
RxJS
RxJS是微软推出的ReactiveX系列,符合纯函数特点的第三方开源库有非常著名underscore和lodash,以及更加强大的RxJS。它可以用来优雅地处理异步和事件。主要通过它的核心类型Observable,以及强大的操作符 (map、filter、reduce、every等,其中大部分都是纯函数)来实现。
官方给它最直白的定义是:可以把 RxJS 当做是用来处理事件的 Lodash 。
使用RxJS的代码消除了一些中间变量,使用操作符来分步执行逻辑,可读性更强、耦合性更低,更方便测试和修改。关于RxJS在web端和node.js服务端的应用都不乏文章,这一次突破常规,来讲一讲在微信小程序开发中的使用。
小程序
直接在小程序中使用RxJS是会报错的,所以我建立了一个开源项目来解决这个问题:RxWX(项目地址:https://github.com/yalishizhude/RxJS )。
封装了两个js文件。
- Rx.js。对Rx.js进行了一些修改使其能在小程序中运行。
- RxWX.js。基于Rx.js对微信的api进行了封装,调用同名API不再使用回调,而是返回Observalbe对象。
安装
提供两种安装途径
- Github
- git clone https://github.com/yalishizhude/RxWX.git
可以直接下载项目,将根目录的Rx.js和RxWX.js复制到小程序项目中,也可以访问该网址复制粘贴这两个文件内容。
npm npm i rxjs-wx 将node_modules/rxjs-wx目录下的Rx.js和RxWX.js复制到小程序项目中。
使用
小程序的API大多数都不是按照纯函数的思想设计的,把返回结果赋值给入参的success、fail、complete属性。
在逻辑简单复杂的情况下很容堕入“回调地狱”,而且同步和异步的接口调用方式也不一致。而使用RxJS就可以解决这些问题,下面来看几个例子。
处理回调
假设有这样一个需求,先通过 wx.getUserInfo 获取用户信息,然后传给后端服务获取该用户其它信息,显示在页面上。
// 普通代码let self = thiswx.getUserInfo({ success: (res) => {
wx.request({ method: 'GET', url: 'xxx/user', data: res.userInfo,
success(r) {
self.setData({userInfo:r})
},
fail(e) {
self.setData({userInfo:'not found'})
}
})
},
fail(e) { console.error(e)
}
})// 使用RxWXimport obs from './RxWX'obs.getUserInfo()
.catch(e => console.error(e))
.switchMap(({ userInfo }) => obs.request({ method: 'GET', url: 'xxx/user', data: user }))
.subscribe(userInfo => self.setData({ userInfo: r }), e => self.setData({ userInfo: 'not found' }))
处理事件
曾经在开发小程序的时候使用navigator组件碰到一个比较严重的问题:快速多次点击的时候会发生多次页面跳转,跳转完成后需要多次点击“返回”才能退回到原页面。
为了解决这个问题,一般可以手动绑定事件,然后进行一个防抖操作。
// 普通代码let tapping = false...
tap(e) { if(!tapping) {
wx.navigateTo({ url: '../demo/demo' })
tapping = true
setTimeout(() => tapping=false, 1000)
}
}// 使用RxWXimport obs from './RxWX'tap(e) {
obs.navigateTo({ url: '../demo/demo' })
.debounce(1000)
.subscribe()
}
RxWX使用场景
微信小程序SDK版本:1.7.0 微信开发者工具版本:1.01
演示项目下载地址: https://github.com/yalishizhude/RxWX/tree/master/example
Get started 按照RxWX说明,把Rx.js和RxWX.js文件放入到utils目录下。 其中Rx.js是可运行在小程序中的Rx.js模块,RxWX.js是利用Rx.js对小程序API进行的封装,封装后API函数将返回Observable对象,属性值不变。 使用时必须引入该文件,比如import rxwx from '../../utils/RxWX.js'同步API与异步API
// 原写法
try {
let result = wx.removeStorageSync('xx')
console.log(result)
} catch(e) {
console.error('小程序API发现错误')
}
// 使用RxWX
import rxwx from '../../utils/RxWX.js'
rxwx.removeStorageSync('xx')
.catch((e) => console.error('RxWX发现错误'))
.subscribe((resp) => console.log(resp))
看上去好像没太大区别。别着急,接着我们来继续看看异步API的调用~
// 原写法
wx.removeStorage({
key: 'xx',
success: function(res) {
console.log(res)
},
error: function(e) {
console.error('小程序API发现错误')
}
})
// 引用RxWX,rxwx具有wx的所有函数和值,但是调用函数返回的是Observable对象
import rxwx from '../../utils/RxWX.js'
rxwx.removeStorage({key: 'xx'})
.catch((e) => console.error('RxWX发现错误'))
.subscribe((resp) => console.log(resp)
在调用同步时RxWX没有太大优势,但在调用异步API的时候以流的方式来处理结果和异常,显然优于回调。而且代码内容和同步调用方式相比并无变化(只修改了函数名和入参)。
这种统一的操作方式可以让开发者更好的关注业务逻辑,而不需要去分辨API到底是异步还是同步,执行结果到底是在回调中获取还是返回值获取。
这种处理方式是不是让你想起点什么?欢迎留言讨论~
嵌套登录后获取用户信息
// 调用小程序原生API
wx.login({
success(res) {
wx.getUserInfo({
success(res) {
console.log(res.userInfo)
},
fail(e) {
console.error(e)
}
})
},
fail(e) {
console.error(e)
}
})
// 调用RxWX
import rxwx from '../../utils/RxWX.js'
rxwx.login()
.switchMap(() => rxwx.getUserInfo())
.catch(e => console.error(e))
.subscribe(res => console.log(res.userInfo))
原生写法很容易坠入回调地狱,而利用RxWx的代码直观易读。
合并 同时获取用户信息和系统信息后进行操作。
// 调用小程序API
let getUser = new Promise((success, fail) => {
wx.getUserInfo({
success,
fail
})
})
let getSystem = new Promise((success, fail) => {
wx.getSystemInfo({
success,
fail
})
})
Promise.all([getUser, getSystem])
.then((resp) => console.log(resp), e => console.error(e))
// 调用RxWX
import rxwx, {Rx} from '../../utils/RxWX.js'
Rx.Observable.zip(rxwx.getUserInfo(), rxwx.getSystemInfo())
.catch(e => console.error(e))
.subscribe(resp => console.log(resp)
其他
RxWX同时还支持wx对象的其它非函数属性,比如:
import obs from './RxWX'console.log(obs.version)
// {info:"", updateTime:"2017.7.10 19:35:05", version:"1.4.0"}
最后
RxJS和RxWX是第三方库,也是进入纯函数世界的大门,更是一种编写更好代码的思维方式。
- 危险:会话固定攻击漏洞,你们的系统都堵上了吗?
- 深度学习入门者必看:25个你一定要知道的概念
- 如何在CM中启用YARN的使用率报告
- 如何修改CDSW服务的DNS和HOSTNAME
- 想尝试搭建图像识别系统?这里有一份TensorFlow速成教程
- 如何查看集成Sentry后Hive作业的真实用户
- 如何在Impala中实现拉链表
- 多图见证模拟机器人的逆天成长:论进化策略在强化学习中的应用
- Hue禁止用户下载数据问题分析
- 如何在RedHat6上使用Bind搭建DNS服务
- 如何使用Cloudera Manager升级Spark2.1版本至Spark2.2
- Cloudera Manager分发Parcel异常分析
- 如何在Hue中配置Impala的负载均衡
- 35行代码实现千万级别字典的快速去重
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【技术文】SSL握手中的几个密码
- 分布式监控系统SkyWalking
- 文献笔记二十九:银合欢(Leucaena trichandra)线粒体基因组
- Docker Swarm群集配置实战——第一战
- 文献笔记五十:vcf2poptree根据vcf文件构建进化树的网页工具
- typescript基础篇(6):泛型
- Flutter 初学者必读的高级布局规则
- Docker Swarm群集配置实战——第二战
- Python 基础(五):序列
- OpenCV黑魔法之隐身衣 | 附源码
- SQL 统计用户留存
- 常用的前端JQ插件
- 面向对象编程(设计模式)需要遵循的 6 个基本原则
- SAP CRM Application Extension Tool的Custom Behavior
- Python 基础(四):字符串