如何从 jQuery 转换到 React.js?

最近几天我一直在看《反应》杂志。我可以理解我所看到的大部分内容,但我对自己的写作能力没有完全的信心。我一直在开发一个小型 web 应用程序,它通过 jQuery 和相互附加元素来生成所有的 html 文件。我想尝试用 React 重建它,因为我相信它会更快。这个 JSFiddle是我正在做的事情的一个小例子。用 React 怎么写?

约翰逊:

function remove() {
this.remove();
}


function timed_append_new_element() {
setTimeout(function () {
var added_content = $("<span />", {
class: "added_content",
text: "Click to close",
click: remove
});
container.append(added_content);
}, 3000);
}


function append_new_element() {
var added_content = $("<span />", {
class: "timed_added_content",
text: "Click to close",
click: remove
});
container.append(added_content);
}




var container = $("<div />", {
class: "container"
});
var header = $("<span />", {
class: "header",
text: "jQuery to React.js Header"
});
var add_button = $("<button />", {
class: "add_button",
text: "Add Element",
click: append_new_element
});
var timed_add_button = $("<button />", {
class: "add_button",
text: "Add Element in 3 seconds",
click: timed_append_new_element
});
container.append(header);
container.append(add_button);
container.append(timed_add_button);
$("body").append(container);
47848 次浏览

有一些基本原则可以帮助你构建一个好的 React 应用程序:

UI 应该是数据的函数

在许多“ jQuery 靓汤”样式的应用程序中,应用程序的业务逻辑、应用程序的数据和 UI 交互代码都是混合在一起的。这使得这些类型的应用程序难以调试,特别是难以扩展。与许多现代客户端应用程序框架一样,React 强调 UI 只是数据的表示。如果希望更改 UI,应该更改一段数据,并允许框架使用的任何绑定系统为您更新 UI。

在 React 中,每个组件(理想情况下)是两个数据片段的函数——传递给组件实例的 物业和组件内部管理的 国家。给定相同的属性(或“道具”)和状态,组件应该以相同的方式呈现。

这可能是一个抽象的想法,没有具体的例子,所以请记住,因为我们现在继续。

别碰 DOM

在 React 中,与其他数据绑定框架相比,您应该尽可能不直接操作 DOM。React 的许多性能和复杂性特征之所以成为可能,是因为 React 在内部使用了带有不同算法的虚拟 DOM 来操作实际的 DOM。任何时候,当您构建一个组件并执行它自己的 DOM 操作时,您都应该问自己,是否可以使用 React 的虚拟 DOM 特性更习惯地构建相同的特性。

当然,有时您需要访问 DOM,或者您需要合并一些 jQuery 插件,而不需要在 React 中重新构建它。在这种情况下,React 提供了良好的 组件生命周期挂钩,您可以使用它来确保 React 的性能不会受到太大影响(或者,在某些情况下,防止您的组件发生突变)。

不操作 DOM 与上面的“ UI 作为数据的函数”密切相关。

反转数据流

在大型 React 应用程序中,很难跟踪哪个子组件正在管理某个应用程序数据。出于这个原因,React 团队建议将数据操作逻辑放在一个中心位置。最直接的方法是将回调传递给子组件; Facebook 还开发了一个名为 Flux 的架构,其中包含 自己的网站

创建可组合的组件

很多时候,编写一个大型组件来管理几个状态或几个 UI 逻辑是很有吸引力的。在可能的情况下(在合理的情况下) ,您应该考虑将较大的组件分解为较小的组件,这些组件对单个数据或 UI 逻辑进行操作。这使得扩展和移动应用程序的各个部分变得更加容易。

小心可变数据

因为组件状态只能通过从组件内部调用 this.setState来更新,所以提防可变数据是很有帮助的。当多个函数(或组件!)可能同时更新可变对象; React 可能尝试批处理状态更改,您可能会丢失更新!正如 Eliseu Monar 在注释中提到的,在对可变对象进行变异之前,请考虑克隆它们。反应有 永恒不变的助手可以协助。

另一种选择是完全放弃将可变数据结构直接保持在状态中; 上面提到的 Flux 模式是对这种想法的一种有趣尝试。


在 React 网站上有一篇很棒的文章叫做 反应中思考,它介绍了如何将一个想法或者一个模型变成一个 React 应用程序,我强烈建议你去看看。作为一个具体的例子,让我们看看您提供的代码。您实际上只需要管理一段数据: 存在于 container元素中的内容列表。对 UI 的所有更改都可以通过对该数据的添加、删除和更改来表示。

通过应用上述原则,您的最终应用程序可能看起来像这样:

/** @jsx React.DOM */


var Application = React.createClass({
getInitialState: function() {
return {
content: []
};
},


render: function() {
return (
<div className="container">
<span className="header">jQuery to React.js Header</span>
<button className="add_button"
onClick={this.addContent}>Add Element</button>
<button className="add_button"
onClick={this.timedAddContent}>Add Element in 3 Seconds</button>
{this.state.content.map(function(content) {
return <ContentItem content={content} removeItem={this.removeItem} />;
}.bind(this))}
</div>
);
},


addContent: function() {
var newItem = {className: "added_content", text: "Click to close"},
content = this.state.content,
newContent = React.addons.update(content, {$push: [newItem]});
this.setState({content: newContent});
},


timedAddContent: function() {
setTimeout(function() {
var newItem = {className: "timed_added_content", text: "Click to close"},
content = this.state.content,
newContent = React.addons.update(content, {$push: [newItem]});
this.setState({content: newContent});
}.bind(this), 3000);
},


removeItem: function(item) {
var content = this.state.content,
index = content.indexOf(item);
if (index > -1) {
var newContent = React.addons.update(content, {$splice: [[index, 1]]});
this.setState({content: newContent});
}
}
});


var ContentItem = React.createClass({
propTypes: {
content: React.PropTypes.object.isRequired,
removeItem: React.PropTypes.func.isRequired
},


render: function() {
return <span className={this.props.content.className}
onClick={this.onRemove}>{this.props.content.text}</span>;
},


onRemove: function() {
this.props.removeItem(this.props.content);
}
});


React.renderComponent(<Application />, document.body);

您可以在 这个 JSFiddle: http://jsfiddle.net/BinaryMuse/D59yP/中看到正在运行的代码

该应用程序由两个组件组成: 一个名为 Application的顶级组件(在其状态下)管理名为 content的数组,另一个名为 ContentItem的组件(表示该数组中单个项的 UI 和行为)。Applicationrender方法为内容数组中的每个项返回一个 ContentItem元素。

需要注意的一点是,管理 content数组内部值的所有逻辑都在 Application组件中处理; ContentItem组件被传递一个 参考文献ApplicationremoveItem方法,单击该方法时,ContentItem会委托该方法。这保留了顶级组件中操作状态的所有逻辑。