|
| 1 | +--- |
| 2 | +layout: docs |
| 3 | +title: 范特西 |
| 4 | +section: zh |
| 5 | +position: 4 |
| 6 | +--- |
| 7 | + |
| 8 | +# xReact 函数式范特西 |
| 9 | + |
| 10 | +xReact 2.3.0 后加入了一个新功能,那就是一个新的类型 `FantasyX`。这个类型,会彻底改变编写前端控件的方式。 |
| 11 | + |
| 12 | +> 如果接触过 PureScript 的同学,估计已经对一个叫 [Flare](https://david-peter.de/articles/flare/)的库有所了解,`FantasyX` 正是 Flare 在 xReact 的实现,但是不同的是,xReact 并没有把 UI 和逻辑揉到一起定义。 |
| 13 | +
|
| 14 | +## 只需一个例子 |
| 15 | + |
| 16 | +想象一下我们要实现一个函数来做乘法操作,如果不涉及 UI,只是输入两个数,输出结果,会是多么简单的一件事情: |
| 17 | + |
| 18 | +```js |
| 19 | +// Number -> Number -> Number |
| 20 | +function mult(a, b) { |
| 21 | + return a * b |
| 22 | +} |
| 23 | +mult(1, 2) |
| 24 | +``` |
| 25 | + |
| 26 | +但是如果我们要写成 UI 控件, 将会变得多么复杂: |
| 27 | + |
| 28 | +- 两个输入控件 `<input/>` |
| 29 | +- 一个输出控件用来显示结果 |
| 30 | +- 绑定函数到 input 的 onChange 上,一旦任何一个 input 发生变化, 则回调函数 |
| 31 | +- 还要从函数获取两个 input 的值作为输入 |
| 32 | + |
| 33 | +天哪, 为什么前端程序员每天都要干这么蠢的事情. 就算使用 React 来做组件,这些过程也一样不能少. |
| 34 | + |
| 35 | +所以才有了 `FantasyX`, 他帮助你构造一起 UI 组件的 Monad, Applicative, Functor, Monoid, 如果你没听过这些词, 没关系,例子会解释一切. |
| 36 | + |
| 37 | +还是实现乘法, 非常简单,只需要把普通函数 lift 起来: |
| 38 | + |
| 39 | +```js |
| 40 | +// FantasyX -> FantasyX -> FantasyX |
| 41 | +let xMult = lift2(mult) |
| 42 | +``` |
| 43 | + |
| 44 | +mult 有两个参数, 所以我们需要 `lift2`, 得到的 `xMult` 依然是函数, 但是 你会发现他的类型从 接收 Number 并返回 Number 的函数,变成了接收 FantasyX 返回 FantasyX 的函数. 这种操作就叫做 `lift` |
| 45 | + |
| 46 | +有了这个函数, 我们需要找输入了, `mult` 需要两个为 Number 的输入, 那么我们的 FantasyX 类型的输入怎么给呢? |
| 47 | + |
| 48 | +```js |
| 49 | +let XMult = xMult(xinput('a'), xinput('b')) |
| 50 | +``` |
| 51 | + |
| 52 | +擦,不能这么简单吧. `xinput` 又是什么鬼? |
| 53 | + |
| 54 | +你管,反正这里的 xinput 帮你构造了一个 FantasyX, 在里面编制着炫酷的数据流. |
| 55 | + |
| 56 | +可能是这样的 |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | +或者是这样的 |
| 61 | + |
| 62 | +.gif) |
| 63 | + |
| 64 | +自己看源码吧. 不过看之前可能你会需要了解 [State Monad](https://github.com/reactive-react/xreact/blob/master/src/fantasy/state.ts) |
| 65 | + |
| 66 | +要知道, 到此为止我们得到的 `XMult` 依然是 FantasyX 类型. 先要得到一个正常的 React Component, 只需要 apply 到一个 React Component 上. |
| 67 | + |
| 68 | + 一个简单 Stateless Component |
| 69 | +```js |
| 70 | +const View = props => ( |
| 71 | + <div> |
| 72 | + <input name="a" onChange={props.actions.fromEvent} defaultValue={props.a}/> |
| 73 | + <input name="b" onChange={props.actions.fromEvent} defaultValue={props.b}/> |
| 74 | + <div>{props.output}</div> |
| 75 | + </div> |
| 76 | +) |
| 77 | +View.defaultProps = { a: "", b: "",output:""} |
| 78 | +``` |
| 79 | + |
| 80 | +apply 到 View 上获得 React Component 一枚 |
| 81 | +```js |
| 82 | +let Mult = XMult.apply(View) |
| 83 | +``` |
| 84 | + |
| 85 | +完整代码: https://www.webpackbin.com/bins/-KoGxSJ-3pOi4DicUvaq |
| 86 | + |
| 87 | +<iframe src="https://www.webpackbin.com/bins/-KoGxSJ-3pOi4DicUvaq" frameborder="0" width="100%" height="500"></iframe> |
| 88 | + |
| 89 | +## Summary |
| 90 | + |
| 91 | + 使用 FantasyX, 我们很简单将逻辑从 UI 中分离 |
| 92 | + |
| 93 | + - 写一个一般的简单函数, lift 之, 应用到 `xinput` 或者其他任何 `FantasyX` 类型 |
| 94 | + - apply 到 View 上, 得到正常 ReactComponent |
| 95 | + |
| 96 | + 不但如此, 你还可以简单的变换或者合并 FantasyX |
| 97 | + |
| 98 | + - FantasyX 实现 Functor, 让你可以 从一个 FantasyX 轻松 map 到另一个 FantasyX |
| 99 | + - FantasyX 实现 Monoid, 简单的 concat 两个 FantasyX, 合成一个 FantasyX, 就跟数组 concat 一样. |
| 100 | + |
| 101 | + [更多...](http://xreact.oyanglul.us/) |
0 commit comments