原文地址:http://www.moye.me/2016/06/16/learning_rxjs_part_two_cycle-js/

是什么

Cycle.js 是一个极简的JavaScript框架(核心部分加上注释125行),提供了一种函数式,响应式的人机交互接口(以下简称HCI):

函数式

Cycle.js 把应用程序抽象成一个纯函数 main(),从外部世界读取副作用(sources),然后产生输出(sinks) 传递到外部世界,在那形成副作用。这些外部世界的副作用,做为Cycle.js的插件存在(drivers),它们负责:处理DOM、提供HTTP访问等。

响应式

Cycle.js 使用 rx.js 来实现关注分离,这意味着应用程序是基于事件流的,数据流是 Observable 的:

HCI

HCI 是双向的对话,人机互为观察者:

在这个交互模型中,人机之间的信息流互为输出输出,构成一个循环,也即 Cycle这一命名所指,框架的Logo更是以莫比乌斯环贴切的描述了这个循环。

唯一的疑惑会是:循环无头无尾,信息流从何处发起?好问题,答案是:

However, we need a .startWith() to give a default value. Without this, nothing would be shown! Why? Because our sinks is reacting to sources, but sources is reacting to sinks. If no one triggers the first event, nothing will happen.  —— via examples

有了.startWith() 提供的这个初始值,整个流程得以启动,自此形成一个闭环,一个事件驱动的永动机 :)

Drivers

driver 是 Cycle.js 主函数 main()和外部世界打交道的接口,比如HTTP请求,比如DOM操作,这些是由具体的driver 负责的,它的存在确保了 main()的纯函数特性,所有副作用和繁琐的细节皆由 driver来实施——所以 @cycle/core 才125 行,而 @cycle/dom 却有 4052 行之巨。

driver也是一个函数,从流程上来说,driver 监听sinksmain()的输出)做为输入,执行一些命令式的副作用,并产生出sources做为main()的输入。

DOM Driver

即 @cycle/dom,是使用最为频繁的driver。实际应用中,我们的main()会与DOM进行交互:

  • 需要传递内容给用户时,main()会返新的DOM sinks,以触发domDriver()生成virtual-dom,并渲染
  • main()订阅domDriver()的输出值(做为输入),并据此进行响应

组件化

每个Cycle.js应用程序不管多复杂,都遵循一套输入输出的基本法,因此,组件化是很容易实现,无非就是函数对函数的组合调用

实战

准备工作

安装全局模块

  1. npm install -g http-server

依赖模块一览

  1. "devDependencies": {
  2. "babel-plugin-transform-react-jsx": "^6.8.0",
  3. "babel-preset-es2015": "^6.9.0",
  4. "babelify": "^7.3.0",
  5. "browserify": "^13.0.1",
  6. "uglifyify": "^3.0.1",
  7. "watchify": "^3.7.0"
  8. },
  9. "dependencies": {
  10. "@cycle/core": "^6.0.3",
  11. "@cycle/dom": "^9.4.0",
  12. "@cycle/http": "^8.2.2"
  13. }

.babelrc (插件支持JSX语法)

  1. {
  2. "plugins": [
  3. ["transform-react-jsx", { "pragma": "hJSX" }]
  4. ],
  5. "presets": ["es2015"]
  6. }

Scripts(热生成和运行服务器)

  1. "scripts": {
  2. "start": "http-server",
  3. "build": "../node_modules/.bin/watchify index.js -v -g uglifyify -t babelify -o bundle.js"
  4. }

以下实例需要运行时,可以开两个shell,一个跑热编译,一个起http-server(爱用currently亦可

  1. $ npm run build
  1. $ npm start

交互实例1

HTML代码 (实例2同,略
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>components</title>
  6. </head>
  7. <body>
  8. <div id="container"></div>
  9. <script src="bundle.js"></script>
  10. </body>
  11. </html>
index.js
  1. import Cycle from '@cycle/core'
  2. import { makeDOMDriver, hJSX } from '@cycle/dom'
  3.  
  4. function main({ DOM }) {
  5. const decrement$ = DOM.select('.decrement').events('click').map(_ => -1)
  6. const increment$ = DOM.select('.increment').events('click').map(_ => +1)
  7. const count$ = increment$.merge(decrement$)
  8. .scan((x, y) => x + y)
  9. .startWith(0)
  10. return {
  11. DOM: count$.map(count =>
  12. <div>
  13. <input type="button" className="decrement" value=" - "/>
  14. <input type="button" className="increment" value=" + "/>
  15. <div>
  16. Clicked {count} times~
  17. </div>
  18. </div>
  19. )
  20. }
  21. }
  22.  
  23. Cycle.run(main, {
  24. DOM: makeDOMDriver('#container'),
  25. })

不难看出:

  • main()是个纯函数,从始至终不依赖外部状态,它的所有动力来自于DOM事件源click,这个状态机依靠Observable.prototype.scan()得以计算和传递,最后生成sinks传递给DOM driver以渲染;
  • 启动了这个循环是 .startWith();
  • Cycle.run是应用程序的入口,加载main()和DOM driver,后者对一个HTML容器进行渲染输出

交互实例2

  • 功能: 一个button一个框,输入并点button后,通过Github api搜索相关的Repo,回显总数并展示第一页Repo列表
index.js
  1. import Cycle from '@cycle/core'
  2. import { makeDOMDriver, hJSX } from '@cycle/dom'
  3. import { makeHTTPDriver } from '@cycle/http'
  4.  
  5. const GITHUB_SEARCH_URL = 'https://api.github.com/search/repositories?q='
  6.  
  7. function main(responses$) {
  8. const search$ = responses$.DOM.select('input[type="button"]')
  9. .events('click')
  10. .map(_ => { return { url: GITHUB_SEARCH_URL } })
  11.  
  12. const text$ = responses$.DOM.select('input[type="text"]')
  13. .events('input')
  14. .map(e => { return { keyword: e.target.value } })
  15.  
  16. const http$ = search$.withLatestFrom(text$, (search, text)=> search.url + text.keyword)
  17. .map(state => { return { url: state, method: 'GET' } })
  18.  
  19. const dom$ = responses$.HTTP
  20. .filter(res$ => res$.request.url && res$.request.url.startsWith(GITHUB_SEARCH_URL))
  21. .mergeAll()
  22. .map(res => JSON.parse(res.text))
  23. .startWith({ loading: true })
  24. .map(JSON => {
  25. return <div>
  26. <input type="text"/>
  27. <input type="button" value="search"/>
  28. <br/>
  29. <span>
  30. {JSON.loading ? 'Loading...' : `total: ${JSON.total_count}`}
  31. </span>
  32. <ol>
  33. {
  34. JSON.items && JSON.items.map(repo =>
  35. <div>
  36. <span>repo.full_name</span>
  37. <a href={ repo.html_url }>{ repo.html_url }</a>
  38. </div>
  39. )
  40. }
  41. </ol>
  42. </div>
  43. }
  44. )
  45.  
  46. return {
  47. DOM: dom$,
  48. HTTP: http$,
  49. }
  50. }
  51.  
  52. const driver = {
  53. DOM: makeDOMDriver('#container'),
  54. HTTP: makeHTTPDriver(),
  55. }
  56.  
  57. Cycle.run(main, driver)

有了实例1做铺垫,这段代码也就通俗易懂了,需要提示的是:

  • Rx的Observable对象,命名上约定以$符为结束,以示区分
  • Observable.prototype.withLatestFrom()的作用是:在当前Observable对象的事件触发时(不同于 combineLatest),去合并参数的目标Observable对象的最新状态,并传递给下一级Observer
  • 以上项目完整实例,可在 /rockdragon/rx_practise/tree/master/src/web 找到

小结

寥寥数语,并不足以概括Cycle.js,比如 MVI设计模式Driver的编写awesome-cycle 这些进阶项,还是留给看官们自行探索吧。

更多文章请移步我的blog新地址: http://www.moye.me/

学习RxJS:Cycle.js的更多相关文章

  1. RxJS/Cycle.js 与 React/Vue 相比更适用于什么样的应用场景?

    RxJS/Cycle.js 与 React/Vue 相比更适用于什么样的应用场景? RxJS/Cycle.js 与 React/Vue 相比更适用于什么样的应用场景? - 知乎 https://www ...

  2. 学习RxJS: 导入

    原文地址:http://www.moye.me/2016/05/31/learning_rxjs_part_one_preliminary/ 引子 新手们在异步编程里跌倒时,永远会有这么一个经典问题: ...

  3. [Cycle.js] From toy DOM Driver to real DOM Driver

    This lessons shows how we are able to easily swap our toy DOM Driver with the actual Cycle.js DOM Dr ...

  4. Javascript学习记录——原生JS实现旋转木马特效

    昨天学习到了JS特效部分,然后老师讲了旋转木马特效的实现,如上图.不过只是讲了通过点击箭头实现图片的切换,对于点击图片本身以及二者联动却是没有讲解. 本着一颗追求完美的心,今天花费了一个中午终于将整个 ...

  5. jquery.cycle.js简单用法实例

    样式: a{text-decoration: none;} *{;;} /*容器设置*/ .player { width:216px; height:248px; background:url(htt ...

  6. [Cycle.js] The Cycle.js principle: separating logic from effects

    The guiding principle in Cycle.js is we want to separate logic from effects. This first part here wa ...

  7. [Cycle.js] Hello World in Cycle.js

    Now you should have a good idea what Cycle.run does, and what the DOM Driver is. In this lesson, we ...

  8. bootstrap插件学习-bootstrap.dropdown.js

    bootstrap插件学习-bootstrap.dropdown.js 先看bootstrap.dropdown.js的结构 var toggle = '[data-toggle="drop ...

  9. bootstrap插件学习-bootstrap.modal.js

    bootstrap插件学习-bootstrap.modal.js 先从bootstrap.modal.js的结构看起. function($){ var Modal = function(){} // ...

随机推荐

  1. ABP框架详解(二)AbpKernelModule

    AbpKernelModule类是Abp框架自己的Module,它也跟所有其他的Module一样继承自AbpModule,重写PreInitialize,Initialize,PostInitiali ...

  2. 在浏览器的JavaScript里new Date().toUTCString()后,传递给C# DateTime().TryParse()会发生什么?

    Format 1. Sun, 09 Oct 2016 13:24:35 GMT Format 2. Sun, 9 Oct 2016 13:36:09 UTC Format 1 是在IE里面产生的(Wi ...

  3. 在CentOS上搭建svn服务器及注意事项

    系统环境 CentOS 5.9 推荐使用yum install安装,比较简单   一.检查是否已经安装其他版本svn # rpm -qa subversion #卸载svn # yum remove ...

  4. HTTP笔记整理(2)

    四.  http协议之请求 1.http请求由三部分组成,分别是:请求行(request line).请求报头(request header).请求正文(body) (1).  请求行:用来说明请求类 ...

  5. HK一行所见闻

    香港一行 20多年来,未未去过HK,前段时间由于工作关系去了趟HK.感触良多. 一清早,福田过关,做火车,做地铁,一通到了目的地. 总结对那边的印象: 1,所有人都是粤语,包括工作交流.而且他们不怎么 ...

  6. 基于SignalR的web端即时通讯 - ChatJS

    先看下效果. ChatJS 是基于SignalR实现的Web端IM,界面风格模仿的是“脸书”,可以很方便的集成到已有的产品中. 项目官网:http://chatjs.net/ github地址:htt ...

  7. [Asp.net 开发系列之SignalR篇]专题四:使用SignalR实现发送图片

    一.引言 在前一篇博文已经介绍了如何使用SignalR来实现聊天室的功能,在这篇文章中,将实现如何使用SignalR来实现发送图片的功能. 二.实现发送图片的思路 我还是按照之前的方式来讲述这篇文章, ...

  8. 一个空行引起的阿里云负载均衡上部署https证书的问题

    今天在阿里云上购买了WoSign的https证书,在证书签发后,在控制台下载证书文件,一共有2个文件,一个是.key文件(私钥文件),一个是.pem文件(证书文件). 然后在阿里云负载均衡“证书管理” ...

  9. 《BI深入浅出》笔记

    今年的项目涉及到BI的知识点,读了<商业智能深入浅出>,这本书是基于IBM的产品做的,基础知识部分讲的非常成体系.记下来做个备忘: 1. BI简介 1.1 实施方案 1)项目规划: 2)系 ...

  10. java POI实现向Excel中插入图片

          做Web开发免不了要与Excel打交道.今天老大给我一个任务-导出Excel.开始想的还是蛮简单的,无非就是查找,构建Excel,response下载即可.但是有一点不同,就是要加入图片, ...