开发 | 技术高人如何开发小程序?他们用这套方法

文 | 接灰的电子产品

对于我这种「不用 Rx 会死星人」来说,如果一个平台没有 Rx,在上面写代码会很痛苦。

所以,自从我开始开发微信小程序以来,就在一直在研究怎么把 RxJS 引入到微信小程序中。

这几天,我终于有了阶段性成果。那「Rx」为什么加引号?嗯,原因是……经过几天的艰苦奋战,我还是没找到把 RxJS 库正确引入到微信小程序的方法。

实际上,我找了一个替代品:XStream ( https://github.com/staltz/xstream )。这个类库呢,和 RxJS 差不多,但更轻量。

相比 RxJS,XStream 去掉了好多不常用的和重复的操作符,当然写法上也略有区别。用起来,XStream 没有 RxJS 爽,但问题不大。

XStream 的引入

和网上的其他类库比较起来,XStream 引入的步骤不算太烦:

  1. 找一个目录,npm install xstream 一下;
  2. 在小程序工程目录下新建一个 libs 目录,然后再建一个 xstream 目录。
  3. 然后在 node_modules/xstream 目录中把 index.js 拷贝到 libs/xstream 下。
  4. node_modules/symbol-observable/lib 中,把 index.jsponyfill.js 都拷贝到 libs/xstream 下。
  5. index.js 改名成 symbol-observable.js,要不然,就会遇到重名问题。
  6. 如果你需要一些其他操作符,可以去 node_modules/xstream/extra 中找,找到后把相应的 JS 文件(比如 debounce.js)拷贝到 libs/xstream/extra 中。

好了,XStream 的引入至此已经完毕,我们看看,如何在小程序工程中使用 XStream 吧。

先来体验一下什么是流式编程。在 pageParams.onLoad 中加上如下代码——当然,别忘了引入 XStream。

到 Console 中看一下,输出结果为 04completed

我们来手动复原一下过程,首先 xs.periodic(1000),是这样一个流:

  • 第一秒时,发射 00 是偶数,满足 filter 条件,进入转换。0 的平方还是 0,结束条件未满足,于是输出 0
  • 第二秒时,发射 11 为奇数,被淘汰;
  • 第三秒时,发射 22 是偶数,满足 filter 条件,进入转换。2 的平方是 4,结束条件未满足,于是输出 4
  • 第四秒时,发射 33 为奇数,被淘汰;
  • 第五秒时,输出 44 是偶数,满足 filter 条件,进入转换。4 的平方是 16,但结束条件已满足,输出 completed

这个小例子虽然简单,但是涉及到了多个流式编程的操作符。这种串(chain)起来的感觉真是很爽。

微信小程序中的响应式编程

由于微信小程序的基于回调函数的设计,我们需要对其 API 进行封装后使其具备响应式编程的能力。

首先,没用 XStream 的时候,代码是下面这样的。

接下来,我们用 XStream 改造一下吧:

天啊,这比原来代码还多,怎么回事?

先别急,前面的一大部分代码,是在将传统的函数改造成流式的函数

这些改造工作如果在普通的 HTML+Javascript 环境中是很好解决的,因为不论是 RxJS 还是 XStream,都提供了转换类操作符,可以方便的帮我们进行转换。

但现在不行啊,这些老外的类库写的时候肯定不会考虑微信的。那怎么办?只好自己写吧。

还是这个例子,我们创建一个叫 http.js 的文件。在这里,我们对应 4 种网络请求方法(GETPOSTPUTDELETE),分别构造了专门的函数用语转换。

工具类建好之后,我们的 onLoad 函数就变得很简单了,是吧?

你想了一下跟我说:你在逗我吗?我不用 XStream 也可以这样封装,代码也会简洁很多啊。

别急,我们费这么大劲把它转换成流式函数,不是只是为了简洁,而是能够使用响应式编程更多特性

比如,上面的代码我们加一个需求:在出错后再进行若干次重试,但需要控制总用时。这个需求很常见,但是常规写法很复杂。

我们看看用响应式编程方式怎么做。

上面代码中,我们每隔一秒(periodic(1000)),输出一个从 0 开始、每次增长 1 的自然数。

接着,在转换函数中生成一个 1-10 的随机数。如果前面数据流发射的数大于这个随机数,我们就手动抛出一个异常,反之原样返回这个数字。

定义好这个数据流后,我们按需求进行处理:

  1. 遇到异常应该重试,那我们使用 replaceError((err) => demo$),每次遇到异常,我们都再执行一遍前面的数据流;
  2. 我们应该控制超时时间 10 秒,所以使用 .endWhen(xs.periodic(10000))。

这样,我们就轻松地解决了这个问题。

我们来看看输出,一开始从 0 到 3 都比较正常,然后程序抛出了异常。replaceError((err) => demo$) 捕获到这个异常,并且用 demo$ 替换错误,也就是说再次执行。

慢着,那不是死循环了吗?没事,我们设定了一个退出条件,就是 10 秒结束该流。

在这个过程中,我们需要注意:在 XStream 中所有的流默认都是 Hot Observable。

怎么理解这个概念呢?

想象一下,我们在看电视直播,我们所有的人不管你是什么时候打开的电视,我们开的内容、进度都是一样的。这就是 Hot Observable。

但 Cold Observable 并不一样,相当于是网络视频。你看到第 20 分钟后我才打开这个视频,这个时候,我的观看进度是从头开始的。

下面是用 RxJS 写的一个每隔 1 秒生成一个增长 1 的自然数流,第二个用户在前一个用户 2 秒之后开始使用。我们会看到下面的情况。

同样的逻辑,用 XStream 实现的代码,出来的是另一番景象。

当然在很多场景中,这种差别不会带来本质的变化。比如 HTTP 请求,本身就是一次性的请求,所以 hot 和 cold 的结果是一样的。

RxJS 作为大而全的类库,当然会同时支持 Hot Observable 和 Cold Observable 的。

XStream 的作者其实也是 RxJS 的 contributor(贡献者)。但他认为,在 web 前端领域,hot 的应用频率远比 cold 要多,所以做了这个精简版的响应式类库。

事件的处理

上述方法用于普通 API 的封装一点问题也没有,但是在做输入事件时,我遇到了一些小麻烦。

获取输入事件不困难。小程序输入事件,也是绑定在 WXML 中的 <input> 控件中,用 bindinput 来指定一个 eventHandler。我将它定名为 addTodo

标准的微信小程序,可以这样来写事件处理。

如果要把事件截获并以数据流输出的话,我们需要在 onLoad 中进行事件处理函数的定义。

比如下面的代码可以让我们实现对于输入事件的定义,在其定义中我们其实使用了流数据的发射作为其函数体。

这样封装后,我们可以使用一些操作符来实现诸如滤波器等功能。

下面的代码片段,就是用于过滤快速输入(小于 400 毫秒)事件的。

但这种的封装有个问题:我们要把这个封装提取为一个单独函数时,由于 this.addTodo 仍未初始化,它就无法作为参数传递,而且 addTodo 也不能写死。

怎么办?我试了几种方案后,选取了使用 Object.defineProperty 的形式,动态定义 pageParams 对象的命名属性的方法。

当然,这个方法还是有一些问题,比如,你仍然需要给这些方法一个初始值(有同学如果有更好的建议请指教)。

下面就是目前实现的抽象封装代码。在下面的代码中,由于我们对外发射的是事件(event),所以其实它不光可以用于输入事件,理论上任意事件都可以。

也就是说,我们自己实现了类似 Rx.Observable.fromEvent 的功能。

最后的话

我为了能在微信顺利使用 XStream,建立了一个 Github 项目,名叫 wxstream (https://github.com/wpcfan/wxstream)。

这名字的意思,其实就是「微信+XStream」。只要把这个项目拉下来,拷贝到微信小程序目录,就立即可用了,包括 XStream 的支持都在里面了。

目前还没什么文档,大部分接口都没测过。后续我逐渐添加文档和进行测试,现在只是个骨架,大家也帮忙测一下吧 ;-)。

原文地址: https://gold.xitu.io/post/5870bd4b61ff4b005c3c4f6e

往期精选文章

本文由知晓程序授权转载,关注微信号 zxcx0101,可获得以下内容和服务:

  • 在微信后台回复「1228」,获得全网第一本《微信小程序入门指南》。
  • 在微信后台回复「加群」,加入「一起发现小程序」微信交流群。
  • 在微信后台回复任意关键词,还能获得相关小程序推荐,赶紧试试吧!

原文发布于微信公众号 - 知晓程序(zxcx0101)

原文发表时间:2017-02-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏有趣的Python

8- vue django restful framework 打造生鲜超市 -商品类别数据展示(下)

Vue+Django REST framework实战 搭建一个前后端分离的生鲜超市网站 Django rtf 完成 商品类别页 vue展示商品列表页数...

2884
来自专栏web前端教室

我在工作中的常用代码管理

说是管理其实就是把常用的一些JS方法,自己保存下来,这样的以后的工作中可以比较方便的使用。 哪些方法可以、或是说值得保存呢?(偏见啊)我自己的主观看法就是一些功...

1735

Eclipse的BIRT:使用Design Engine API

假设您已经在名为“customers”的报告设计文件中将表格定义为报告项目。顾名思义,该表格用于显示示例数据库中的所有客户。此外,它还有一个用于按照国家来对项目...

1322
来自专栏天天P图攻城狮

iOS基础开发实践:iMessage Extension浅析

1452
来自专栏韩伟的专栏

经典软件架构模式(二)

今天继续推送“经典软件架构模式(二)之管道和过滤器模式、MVC模式。 管道和过滤器模式 第三个案例是一个WEB的例子,但并不是简单的CGI加数据库,而是一个在...

2676
来自专栏Google Dart

AngularDart4.0 英雄之旅-教程-08HTTP 顶

你离开的地方 在前一页中,您学会了在仪表板和固定英雄列表之间导航,沿途编辑选定的英雄。 这是这个页面的起点。

633
来自专栏落影的专栏

直播APP的性能优化-礼物篇

介绍 记录、总结开发遇到一些问题,大家一起交流学习。 这次带来,对直播APP性能优化的总结,以QA的形式总结。 欢迎关注文集-直播Live 实现方式 1、Q:...

3016
来自专栏腾讯Bugly的专栏

Android开发必备知识:为什么说Kotlin值得一试

1、Hello, Kotlin 1.1 Kotlin的身世 写了许久 Java,有没有发现其实你写了太多冗余的代码? 后来你体验了一下 Python,有没有觉得...

4089
来自专栏AI科技大本营的专栏

AI 技术讲座精选:如何利用 Python 读取数据科学中常见几种文件?

前 言 如果你是数据行业的一份子,那么你肯定会知道和不同的数据类型打交道是件多么麻烦的事。不同数据格式、不同压缩算法、不同系统下的不同解析方法——很快就会让你...

3454
来自专栏小巫技术博客

应用被强杀了怎么办

602

扫码关注云+社区