2 か月前に React についてどう思うか尋ねられたら、おそらく次のように答えたでしょう:
理解できなかったからです
誓います、React は間違いなく正しい方向に進んでいます。聞いてください。
古き良きMVC
対話型アプリケーションにおける諸悪の根源は管理状態です。
「従来の」方法は、MVC アーキテクチャ、またはそのバリエーションです。
MVC は、モデルを唯一の信頼できる情報源として提示します。すべての状態がそこに存在します。
ビューはモデルから派生するため、同期を保つ必要があります。
モードが変わると視界がなくなります。
最後に、ユーザー インタラクションがコントローラーによってキャプチャされ、モデルが更新されます。
ここまでは順調ですね。
モデルが変更されると、ビューをレンダリングする必要があります
これは非常に単純に見えます。まず、ビューを説明する必要があります。つまり、ビューがモデルの状態を DOM に転送する方法です。次に、ユーザーが何らかの操作を実行するとすぐに、モデルを更新してページ全体を再レンダリングする必要があります。残念ながら、次の 2 つの理由により、これはそれほど簡単ではありません。
データバインディング
データ バインディングは、モデルとビューの同期を自動的に維持します。通常、JavaScript ではオブジェクトと DOM によって表されます。
アプリケーションのさまざまなチャンク間の依存関係を宣言できるようにすることで、この同期をパッケージ化します。状態の変更はアプリケーション全体に伝播し、すべての依存ブロックが自動的に更新されます。
いくつかの有名なフレームワークで実際にどのように動作するかを見てみましょう。
ノックアウトKnockout は、MVVM (Model-View-ViewModel) メソッドの使用を推奨し、「ビュー」部分の実装を支援します。
// View (a template) <p>First name: <input data-bind="value: firstName" /></p> <p>Last name: <input data-bind="value: lastName" /></p> <h2>Hello, <span data-bind="text: fullName"> </span>!</h2> // ViewModel (diplay data... and logic?) var ViewModel = function(first, last) { this.firstName = ko.observable(first); this.lastName = ko.observable(last); this.fullName = ko.pureComputed(function() { // Knockout tracks dependencies automatically. It knows that fullName depends on firstName and lastName, because these get called when evaluating fullName. return this.firstName() + " " + this.lastName(); }, this); };
しかし、モデルが真実のソースではないでしょうか? ここでのビュー モデルは、モデルが変更されたことをどのようにして知るのでしょうか?
角度Angular では、モデルとビューの同期を維持する方法でデータ バインディングを説明します。ドキュメントでは次のように説明されています。
しかし...ビューはモデルと直接通信する必要がありますか?
何はともあれ、Hello World の例を見てみましょう:
从这个示例中,看起来像是控制器有了状态,并且有类似模型的行为 - 或者也许是一个视图模型? 假设模型在其它的地方, 那它是如何保持与控制器的同步的呢?
我的头开始有点儿疼了.
数据绑定的问题
数据绑定在小的例子中运行起来很不错。不过,随着你的应用规模变大,你可能会遇到下面这些问题.
声明的依赖会很快引入循环
最经常要处理的问题就是对付状态中变化的副作用。这张图来自 Flux 介绍,它解释了依赖是如何开始挖坑的:
你能预计到当一个模型发生变化时跟着会发生什么改变么? 当依赖发生变化时,对于可以任意次序执行的代码你很难推理出问题的起因。
模板和展示逻辑被人为的分离
视图扮演了什么角色呢? 它扮演的就是向用户展示数据的角色。视图模型扮演的角色又是什么呢? 它扮演的也是向用户展示数据的角色?有啥不同?完全没有!
最后,视图组件应该能操作其数据并以需要的格式对数据进行展示。然后,所有的模板语言本质上都是有缺陷的:它们从来都不能达到跟代码一样的表现力和功能。
很简单, {{# each}}, ng-repeat 和 databind="foreach" 这些都是针对 JavaScript 中某些原生和琐碎事务的拙劣替代物。而它们不会更进一步走得更远。因此它们不会为你提供过滤器或者映射。
数据绑定是应重新渲染而生的小技巧
什么是圣杯不再我们的讨论之列。每个人总是想要得到的是,当状态发生变化时能重新对整个应用进行渲染。这样,我们就不用去处理所有麻烦问题的根源了:状态总是会随着时间发生变化——给定任何特定的状态,我们就可以简单的描述出应用回是什么样子的。
好了,问题清楚了。哥们,我希望某些大公司能组个超能天才开发者团来真正解决这个问题...
拥抱Facebook的React
事实证明他们做到了。React实现了一个虚拟的DOM,一种给我们带来的圣杯的利器.
虚拟的DOM是啥东西呢?
很高兴你能这么问?让我们来看看一个简单React示例.
var Hello = React.createClass({ render: function() { return <div>Hello {this.props.name}</div>; } }); React.render(<Hello name="World" />, document.getElementById('container'));
这就是一个React组件的所有API。你必须要有一个渲染方法。复杂吧,呵呵?
OK, 但是
你的新伙伴,JSX
这段代码实际上是用 JSX 写的,它是 JavaScript 的一个超集,包含了用于定义组件的语法。上面的代码会被编译成 JavaScript,因此实际上会变成:
var Hello = React.createClass({displayName: "Hello", render: function() { return React.createElement("div", null, "Hello ", this.props.name); } }); React.render(React.createElement(Hello, {name: "World"}), document.getElementById('container'));
你明白这段对 createElement 调用的代码么? 这些对象组成了虚拟 DOM 的实现。
很简单 : React 首先在内存中对应用的整个结构进行了组装。然后它会把这个结构装换成实际的 DOM 节点并将其插入浏览器的 DOM 中。
OK,但是用这些奇怪的 createElement 函数编写 HTML 的目的是什么呢?
虚拟的DOM就是快
我们已经讨论过, 操作 DOM 消耗大得离谱,因此它必须以尽可能少的时间完成。
React 的虚拟 DOM 使得两棵 DOM 结构的比对真正快起来,并且能确切的找到它们之间有什么变化. 如此,React 就能计算出更新 DOM 所需要做出的最小变更。
实话说,React 能比对两棵 DOM 树,找出它所要执行的最小操作集。这有两个意义:
那我们还要在状态发生变化时记住这两个对整个 app 进行重新渲染的问题么?
这都是过去式了。
React 将状态映射到 DOM
React 中只有虚拟 DOM 的渲染和比对是神奇的部分。其优秀性能是使得我们拥有简化了许多的整理架构的基础。有多简单呢?
React 组件都是幂等(一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同)的函数。它们能在任意一个实时的点来描述你的UI。~ Pete Hunt, React: 对最佳实践的重新思考
简单的幂等函数。
React 组件整个就是这么一个东西,真的。它将当前的应用状态映射到了 DOM。并且你也拥有JavaScript的全部能力去描述你的 UI——循环,函数,作用域,组合,模块 - 不是一个蹩脚的模板语言哦.
var CommentList = React.createClass({ render: function() { var commentNodes = this.props.data.map(function (comment) { return ( <Comment author={comment.author}> {comment.text} </Comment> ); }); return ( <div className="commentList"> {commentNodes} </div> ); } }); var CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.props.data} /> </div> ); } }); React.render( <CommentBox data={data} />, document.getElementById('content') );
今天就开始使用 React
React 一开始会有点令人生畏。它提出了一个实在是太大了点的模式转变,这总有点令人不舒服。不过,当你开始使用它时其优势会变得清楚起来。
React 文档很优秀. 你应该照着教程对其进行一下尝试。我确信如果你给它一个机会,你肯定会爱上她。
编码快乐!