在推闲逛,看到有人转了个Cycle.js的视频教程
Cycle.js是什么东西?
经过一番谷歌,这个东西诞生的也不算太久,灵感应该来自于React,因为它的思路实在是和React太像了...可以看下这个Demo -> http://jsbin.com/vikaga/1/edit?js,output
一篇文章作者这样描述Cycle.js
Cycle.js is clearly React inspired, but it attempts to fix some areas where React is deficient, or weird. It also advocates (forces?) a functional style, composing immutable state via observables.
Cycle.js的官网是这样介绍的
A functional and reactive JavaScript framework for cleaner code
Cycle.js支持Virtual-DOM、服务端渲染、JSX、React Native、等等
看Cycle.js的Github上的介绍说:Note: rx is a required dependency. Without it, nothing will change.
,而且我看Cycle的视频教程第一课也码的是Rx,这是什么?
好吧,最开始我为了图方便,直接百度RxJS
,出来仅有的几个结果简直是让人绝望...
还是问问谷大哥好了,RxJS是微软出品的ReactiveX的一个Javascript语言子集,Rx可以使用相当多的编程语言和平台
ReactiveX是Reactive Extensions的缩写,一般简写为Rx,最初是LINQ的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源,Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,Rx库支持.NET、JavaScript和C++,Rx近几年越来越流行了,现在已经支持几乎全部的流行编程语言了,Rx的大部分语言库由ReactiveX这个组织负责维护,比较流行的有RxJava/RxJS/Rx.NET,社区网站是 http://reactivex.io/
好吧,看来想学Cycle.js得先知道Rx是什么,Rx上面已经知道是微软出的一个处理异步数据流的库,其实,它也是RP的一个实现。RP(Reactive Programming, 响应式编程),是个挺有趣的东西,他的实现有Rx、Bacon.js、Kefir等等,好吧,概念一堆堆的出来,压力稍微有点大...先看看wiki上对于RP的简短解释:响应式编程
说了这么多,这些和FB出的React又是什么关系?
我是这么理解的,他们流派不同,都是响应式编程的一种解决方案,FB的React有Flux,然后RxJS也可以搭配Cycle.js实现相同的事情,都是在做基于数据流的编程(此处比较慌...自己理解,大神路过,如果错误请斧正),关于响应式编程,可以看看这篇文章:RP入门,文章里还实现了一个Twitter Follow的Demo,相当不错:http://jsfiddle.net/staltz/8jFJH/48/
铺垫太多了,其实我就是想记录两个RxJS的小Demo而已的...回归主线
第一次看RxJS的代码是这样的
// Logic
Rx.Observable.timer(0, 1000)
.map( i => `seconds elapsed ${i}`)
// Effects
.subscribe(text => {
const container = document.querySelector('#app');
container.textContent = text;
});
配合上index.html文件
<!DOCTYPE html>
<html>
<head>
<script src="//cdn.bootcss.com/rxjs/4.0.7/rx.all.min.js"></script>
<script src="/1.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>RxJS test</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
用浏览器打开,页面就会从零开始计数,1s加一
这种数据流的方式感觉还是满亲切的,跟Unix的管道概念很是类似,在Rx中的Observable对象也跟Unix的'一切皆文件'有些神似。很难想象是微软竟然搞出的东西,隐隐感觉这Rx要火呢...
其实第一个例子挺不好的,耦合度很高,小小改进一下
// Logic (functional)
function main() {
return Rx.Observable.timer(0, 1000)
.map( i => `seconds elapsed ${i}`);
}
// Effects (imperative)
function DOMEffect(text$) {
text$.subscribe(text => {
const container = document.querySelector('#app');
container.textContent = text;
});
}
DOMEffect(main());
这下把逻辑和显示分开了
不过还是功能写死了,逻辑太单一了,只能输出到界面上,再稍稍改进一下,使其能够同时输出到console里
function main() {
return {
DOM: Rx.Observable.timer(0, 1000)
.map(i => `Seconds elapsed ${i}`),
Log: Rx.Observable.timer(0, 2000)
.map(i => 2*i),
};
}
function DOMEffect(text$) {
text$.subscribe(text => {
const container = document.querySelector('#app');
container.textContent = text;
});
}
function consoleLogEffect(msg$) {
msg$.subscribe(msg => console.log(msg));
}
function run(mainFn) {
const sinks = main();
DOMEffect(sinks.DOM);
consoleLogEffect(sinks.Log);
}
run(main);
经过改进,可以定义不同的效果函数,来展示数据,还是不错的,只是仔细想来,Effect这种叫法不是太准确,使用Driver恰当很多,再者一个问题是,这个程序还是不够灵活,如果想要注释掉一个效果,比如在consoleLogEffect(sinks.Log);
前面加上注释,这么做没问题,不过有更好的方法,来控制哪个Driver运行
function main() {
return {
DOM: Rx.Observable.timer(0, 1000)
.map(i => `Seconds elapsed ${i}`),
Log: Rx.Observable.timer(0, 2000)
.map(i => 2*i),
};
}
function DOMDriver(text$) {
text$.subscribe(text => {
const container = document.querySelector('#app');
container.textContent = text;
});
}
function consoleLogDriver(msg$) {
msg$.subscribe(msg => console.log(msg));
}
function run(mainFn, drivers) {
const sinks = mainFn();
Object.keys(drivers).forEach(key => {
drivers[key](sinks[key]);
});
}
const drivers = {
DOM: DOMDriver,
Log: consoleLogDriver,
}
run(main, drivers);
这样,只需在drivers中进行注释就可以决定哪个运行,比较清晰合理
其实,最近学了几个例子,我感觉Rx我才接触到冰山一角,别说深入一些的思想,就是常用函数和功能都还没搞太清楚...不过,还是要硬着头皮整理一下,不然过一阵还没往下学习就又忘光了...
所以,这里我不知道Rx最适合做什么,也不知道Rx有有什么黑科技,反正Rx很强大,慢慢往下看Demo吧...
RxJS很核心的一个东西就是Observable,它是一个可观测对象,它可以自己创建,可以由事件,数据,甚至能接受Promise对象,比如:
var source = [1, 2, 3, 4, 5];
var source = Rx.Observable.fromArray([0, 1, 2, 3, 4, 5]);
source.filter(x => x % 2 === 1)
.map(x => x + '!')
.forEach(x => console.log(x));
输出为
1!
3!
5!
再比如
console.clear();
var source = Rx.Observable.interval(500).take(10);
source.filter(x => x % 2 === 1)
.map(x => x + '!')
.forEach(x => console.log(x));
输出为
1!
3!
5!
7!
9!
在Rx中,使用好数据流,可以在不使用if/else实现判断,有趣的很
有一本Online Book叫做《rx-book》,是个参考书,可以收藏查阅,并且里面的函数都是提供Demo的,使用的是jsbin,可以修改看效果,很直观方便
再来看看最开始的那个例子,其实subscript中是可以接受参数的,看下面的例子
Rx.Observable.timer(0, 1000)
.map( i => `the time is ${i}`)
.subscribe(text => {
const container = document.querySelector('#app');
container.textContent = text;
}, function(err) {
console.error(err);
}, function() {
console.info('done');
});
在这个中,第一个函数进行控制数据行为,第二个函数进行错误处理,第三个函数在所有的数据都处理完成时输出done
整体而言,首先用Rx创建一个Observable可观察对象,然后将其传给source变量,在Rx中有个叫Observer的对象,是用来专门定义上边代码的中subscribe中的行为函数的,比如
var source = Rx.Observable.return(42);
var observer = Rx.Observer.create(
x => console.log(`onNext: ${x}`),
e => console.log(`onError: ${e}`),
() => console.log('onCompleted'));
var subscription = source.subscribe(observer);
// => onNext: 42
// => onCompleted
这样,结构更加清晰合理了。当然,在Observable的后面,我们使用fromArray([0, 1, 2, 3, 4, 5]);
就输出1,2,3,4,5序列了。更多内置的方法,需要去查阅上面提供的rx-book进行寻找了~
比如这里:http://xgrommx.github.io/rx-book/content/observable/observable_methods/index.html可以找到很多创建Observable的方法,这里http://xgrommx.github.io/rx-book/content/observable/observable_instance_methods/index.html可以找到很多Observable的对象的方法,可以对数据进行过滤,聚合,拆分,等等操作,就像管道一样,这种处理数据的方式还是蛮Cool的
不知道读者注意到上面提到的done,对了,反正我翻看rx-book中Mapping RxJS from Different Libraries
很是吃惊...前几个月学习过的Async流程控制和集合操作竟然可以等效的转移到RxJS...详见:RxJS for Async.js Users
比如async中的each操作是这样的:
var async = require('async'),
fs = require('fs');
var files = ['file1.txt', 'file2.txt', 'file3.txt'];
function saveFile (file, cb) {
fs.writeFile(file, 'Hello Node', err => cb(err));
}
async.each(files, saveFile, err => {
// if any of the saves produced an error, err would equal that error
});
在RxJS中可以这么实现
var Rx = require('rx'),
fs = require('fs');
var files = ['file1.txt', 'file2.txt', 'file3.txt'];
// wrap the method
var writeFile = Rx.Observable.fromNodeCallback(fs.writeFile);
Rx.Observable
.for(files, file => writeFile(file, 'Hello Node'))
.subscribe(
() => console.log('file written!'),
err => console.log(`err ${err}`),
() => console.log('completed!')
);
我此时内心是十分惊讶的...
M¥出品的东西果然是高大上,相比于各种辅助类的工具,Rx感觉像是一个庞然大物,不是一个运用于快速构建的工具,而更像是一个工程级的异步数据流解决方案,在rx-book中还有很多东西没有看,没有深入的学习也不是很了解,不过简单的对比,我感觉React更适合快速开发,随着React Native的发展,也将在移动端大放异彩。而Rx给人一种更加专业化的感觉,作为响应式编程的先行者,作为众多同类技术的灵感来源,Rx是很强大的,在其开源之后,各种语言环境的子分支都有不错的发展,而这也是很难能可贵的。
今天的整理就到这里,至于Cycle.js, 下篇同类型的博文再记录吧...
参考: