翻译至官方文档《Tutorial》http://facebook.github.io/react/docs/tutorial.html

转载请注明出处:http://blog.csdn.net/adousen

推荐阅读 React|RakNet 博客:http://blog.csdn.net/rsspub/article/category/1435601

教程示例代码,Web程序框架采用的是全栈python web框架Uliweb

Uliweb  https://github.com/adousen/reactjs_uliweb_example

在入门教程里,我们会创建一个简单却实用的评论盒子来作为我们的例子,你可以把它放进一个博客什么的。它实际上就是Disqus、LiveFyre、Facebook等实时评论的基础实现。 我们要实现的功能有:

  • 浏览所有的评论
  • 提交一个评论的表单
  • 为你自定义的后端提供一个钩子

此外,还有一些优化特性:

  • 优化评论:在评论保存到服务器前,就在列表中将其显示,这样会感觉更快。
  • 实时更新:当其它用户做出评论后,评论列表就可以得到实时的更新。
  • 支持Markdown格式:用户可以用Markdown格式书写内容。

第一步

在教程中,我们直接使用的是CDN上的Javascript框架文件。 下面,打开任意你喜欢的编辑器,创建一个新的HTML文档:

<!-- template.html -->
<html>
<head>
<title>Hello React</title>
<script src="http://fb.me/react-0.12.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.0.js"></script>
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
// Your code here
</script>
</body>
</html>

此后的教程中,我们都将在这里的script标签内编写JavaScript代码。

注意: 此处我们将jQuery包含了进来,但目的只是为了方便编写ajax调用。但这不是在React中所必须做。

你的第一个组件

React所有的一切都是关于模块化、复合化的组件。就我们的评论功能来说,我们将按照下面的组件结构来实现:

- CommentBox
- CommentList
- Comment
- CommentForm

我们先来创建一个CommentBox组件,它一开始只是一个简单的<div>:

// tutorial1.js
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
React.render(
<CommentBox />,
document.getElementById('content')
);

JSX 语法

首先,你注意到的是Javascript代码中的XML化语法。我们实际上可以使用一个预编译器来将此语法糖转换为纯Javascript:

// tutorial1-raw.js
var CommentBox = React.createClass({displayName: 'CommentBox',
render: function() {
return (
React.createElement('div', {className: "commentBox"},
"Hello, world! I am a CommentBox."
)
);
}
});
React.render(
React.createElement(CommentBox, null),
document.getElementById('content')
);

这是一种可选的方式,但实际上可以发现JSX语法要比单纯的Javascript语法要简单。了解更多有关 JSX 语法的内容。

接下来做什么

下面我们要创建一个新的React组件,采取的方式是向 React.createClass() 传递一个Javascript对象,为组件添加一些方法。其中最重要的一个方法是 render,它会返回一个React组件树,并最终被渲染成HTML。 div 标签并不是真正的DOM节点,它们只是React div组件的实例。你可以把它想象成能由React识别并处理的一些标记或一段数据。React是安全的。我们并不生成HTML字符串,所以默认是XSS保护。 你可以返回一个由你或别人创建的组件树,而不一定要返回基本的HTML。正因如此,React组件可以组合使用的:这是可维护前端的宗旨。 React.render() 初始化了一个根节点组件,然后启动框架,并将标记注入到一个原生DOM元素中。这个DOM元素由第二个参数指定。

组建组件

接着我们创建 CommentList 和 CommentForm 基本骨架,它们同样也是 div。注意,这段代码要放在CommentBox 代码的前面。

// tutorial2.js
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
Hello, world! I am a CommentList.
</div>
);
}
});
 
var CommentForm = React.createClass({
render: function() {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});

下一步,更新 CommentBox 组件的代码,使用新定义的两个朋友:

// tutorial3.js
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList />
<CommentForm />
</div>
);
}
});

注意我们是如何混合使用HTML标签和自建组件的。HTML组件是规范的React组件,与自定义的组件类似,只是有一个差别:JSX编译器会自动将HTML标签重写为 React.createElement(tagName) ,并且不管其它的事情。这是了避免对全局命名空间的污染。

组件属性

我们将创建一个第三方组件 Comment,它负责接收评论者的名字和评论的内容。对于每个单独的评论,我们都可以重用这个组件的代码。首先,我们向 CommentList 添加一些评论。

// tutorial4.js
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
<Comment author="Pete Hunt">This is one comment</Comment>
<Comment author="Jordan Walke">This is *another* comment</Comment>
</div>
);
}
});

注意到,这里我们通过父组件 CommentList 向子组件 Comment 传递了一些数据。比如,我们在一个 Comment中,向其传递了Pete Hunt(通过属性)和一条评论(通过XML格式的子节点)。从父组件向子组件传递的数据被称为props(单词properties的缩写)。

使用props

接下来,我们就来创建这个Comment组件。使用porps我们可以读取从 CommentList 传递的数据,并渲染一些标记。

// tutorial5.js
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{this.props.children}
</div>
);
}
});

在JSX中通过用括号括起来的JavaScript表达式,可以将文本或者React组件(可以是一个属性或子元素)放进组件树中。this.props 和嵌套元素 this.props.children 中的关键字props是传递给组件的命名属性。

添加 Markdowen语法支持

Markdown文本支持内联样式。例如,用星号围起的文本可以强调显示。 首先,我们需要向程序中添加第三方的 Showdown 库。这是一个支持Markdown并将其转换为原始HTML代码的JavaScript库。我们需要向head中添加一段script标签(我们已经包含了一些React的库):

<!-- template.html -->
<head>
<title>Hello React</title>
<script src="http://fb.me/react-0.12.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.0.js"></script>
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script>
</head>

下一步,我们将评论的文本做Markdown转换并输出:

// tutorial6.js
var converter = new Showdown.converter();
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{converter.makeHtml(this.props.children.toString())}
</div>
);
}
});

此处,我们增加了对Showdown库的调用。为了将 this.props.children 从React包装了的文本转换成Showdown可以接受的原始字符串,我们显式地调用了 toString()。 但是,这里有一个问题需要解决。我们最后渲染出来的评论内容在浏览器中看起来却是这样的形式:"<p>This is <em>another</em> comment</p>"。我们想要的是让这些标签都能被渲染为实际的HTML。 这种处理方式是为了防止XSS攻击。有一种方式可以跳过,但是框架会警告你不要使用这种方式。

// tutorial7.js
var converter = new Showdown.converter();
var Comment = React.createClass({
render: function() {
var rawMarkup = converter.makeHtml(this.props.children.toString());
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});

这个特殊的API的目的是让插入原生的HTML代码显得困难,但是为Showdown我们还是利用了这个后门。 记住:使用这个特征时,你必须确定Showdown是安全的。

连接数据模型

目前为止,我们是直接在源代码中插入评论。下面,我们将在评论列表中渲染一段JSON数据。最终,我们将从服务器端获取,但是现在,我们把它直接写在代码中:

// tutorial8.js
var data = [
{author: "Pete Hunt", text: "This is one comment"},
{author: "Jordan Walke", text: "This is *another* comment"}
];

我们需要以编写模块的方式将数据data添加进 CommentList.因此,我们修改 CommentBox 组件 以及 React.render() 调用的代码,将data通过props进行传递。

// tutorial9.js
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.props.data} />
<CommentForm />
</div>
);
}
});
 
React.render(
<CommentBox data={data} />,
document.getElementById('content')
);

现在,data已经被传递进了 CommentList,那么让我们来动态地呈现评论数据:

// tutorial10.js
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>
);
}
});

就是这样!

从服务器读取

下面,我们用从服务器端读取的动态数据来替换硬性编码的数据。我们删除了 data 属性,改为采用 URL 来获取:

// tutorial11.js
React.render(
<CommentBox url="comments.json" />,
document.getElementById('content')
);

这个组件和之前的组件的不同之处在于它必须预先自行渲染。在从服务器端获得请求应答之前,它没有可用的数据,而这些数据是组件呈现评论所必须的。

反应state

到现在为止,所有的组件都只是根据自身的props进行一次性的渲染。props 是不可变的:它们是从父组件传递过来,并且为父组件所有。为了实现交互,我们为组件引入了可变的 state 。this.state属于组件的私有成员,并且可以通过调用 this.setState() 进行修改。当state更新之后,组件会立即对其自身进行重新渲染。 实际上在React代码中,render() 方法被被声明为 this.props 和 this.state 的函数,并由框架保证了UI总是与输入保持一致。 当从服务器取得数据后,就可以对我们的评论数据进行修改。首先,我们向 CommentBox 组件的state添加一个包含评论数据的数组data:

// tutorial12.js
var CommentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});

在组件的生命周期中,getInitialState() 只执行一次,它负责对组件的state进行初始化。

更新state

在组件创建完毕后,我们还想要从服务器GET到JSON,从而更新state来反映最新的数据。在实际的应用中,我们可能创建的是一个动态的应用。但是,在例子中为了简单,还是使用一个静态的JSON文件:

// tutorial13.json
[
{"author": "Pete Hunt", "text": "This is one comment"},
{"author": "Jordan Walke", "text": "This is *another* comment"}
]

我们打算使用jQuery对服务器进行异步的访问。 注意: 由于这是一个AJAX应用,因此你需要在一个web服务器上运行,而不能仍停留在文件系统。最简单的方式是在应用的目录下运行python -m SimpleHTTPServer

// tutorial13.js
var CommentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});

这里的componentDidMount是一个在React组件渲染以后将被React调用的方法。动态更新的关键取决于 this.state的调用。在从服务器取得数据以后,我们就使用新数组替换评论组件的旧数据,并且让它动态地改变。这种反应的方式,使得动态更新只是做了小小的改变。此处,我们使用的投票数据很简单,你也可以很容易使用WebSockets或其它技术来获得。

// tutorial14.js
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});
 
React.render(
<CommentBox url="comments.json" pollInterval={2000} />,
document.getElementById('content')

此处我们做的仅仅是将AJAX调用放到一个独立的方法中,并且在组件第一次加载和此后每隔两秒调用一次。可以尝试在浏览器中运行一下,并且手动修改 comments.json。可以看到,在两秒内变化就被呈现了出来。

添加新的评论

现在,是时候创建一个评论表单了。我们的 CommentForm 组件需要向询问用户他们的名字和评论的内容,并将其发送给服务器进行保存。

// tutorial15.js
var CommentForm = React.createClass({
render: function() {
return (
<form className="commentForm">
<input type="text" placeholder="Your name" />
<input type="text" placeholder="Say something..." />
<input type="submit" value="Post" />
</form>
);
}
});

让我们来创建与表单的交互。当用户点击submit提交以后,我们需要将表单清空,并将一个请求发送到服务器,然后更新评论列表。那么,首先我们需要监听表单的submit事件,并将其清空。

// tutorial16.js
var CommentForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var author = this.refs.author.getDOMNode().value.trim();
var text = this.refs.text.getDOMNode().value.trim();
if (!text || !author) {
return;
}
// TODO: send request to the server
this.refs.author.getDOMNode().value = '';
this.refs.text.getDOMNode().value = '';
return;
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder="Your name" ref="author" />
<input type="text" placeholder="Say something..." ref="text" />
<input type="submit" value="Post" />
</form>
);
}
});

事件Events

React向组件添加的事件处理函数使用的是驼峰命名规则。我们向表单添加了一个 onSumbit 的处理函数,它负责在输入数据合法的表单提交后,将表单的字段清空。 在事件处理中,调用 preventDefault 是为了阻止浏览器默认的与表单提交有关的行为。

Refs

我们使用 ref 属性向子组件分配了一个名字,并且通过 this.refs对组件进行引用。我们可以在一个组件上调用 getDOMNode 获取一个原生DOM元素。

在props中定义回调函数

当用户提交一条评论时,我们还需要对之前的评论列表进行更新,让新的评论显示进来。对于含有与呈现评论有关数据的state的CommentBox 来说,需要定义这样的行为逻辑。 我们需要从子组件传送数据到它的父组件。我们在父组件的 render 方法中将一个新的回调函数(handleCommentSubmit)传递给子组件,并将其绑定在子组件的 onCommentSubmit 事件上。当事件被触发后,回调函数就会被执行。

// tutorial17.js
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
// TODO: submit to the server and refresh the list
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});

当用户提交表单的时候,我们就从 CommentForm 调用回调函数。

// tutorial18.js
var CommentForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var author = this.refs.author.getDOMNode().value.trim();
var text = this.refs.text.getDOMNode().value.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.refs.author.getDOMNode().value = '';
this.refs.text.getDOMNode().value = '';
return;
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder="Your name" ref="author" />
<input type="text" placeholder="Say something..." ref="text" />
<input type="submit" value="Post" />
</form>
);
}
});

现在,回调函数已经定义完毕。我们要做的就是提交新的评论到服务器,并刷新评论列表。

// tutorial19.js
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});

优化:优化更新

现状我们已经实现了这个应用的所有功能。但是,在从服务器完成请求之前,你必须等待评论在列表中出现。因此,会感觉有点慢。我们可以对它再做一点优化,让它感觉更快一点。

// tutorial20.js
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.data;
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});

祝贺你!

通过一些简单的步骤,你已成功创建了一个评论盒子。你可以了解更多有关为什么使用React 或者深入的学习 API参考。 祝你顺利!

React JS快速入门教程的更多相关文章

  1. .NET Core快速入门教程 2、我的第一个.NET Core App(Windows篇)

    一.前言 本篇开发环境?1.操作系统: Windows 10 X642.SDK: .NET Core 2.0 Preview 二.安装 .NET Core SDK 1.下载 .NET Core下载地址 ...

  2. .NET Core快速入门教程 3、我的第一个.NET Core App (CentOS篇)

    一.前言 本篇开发环境?1.操作系统:CentOS7(因为ken比较偏爱CentOS7)2.SDK版本:.NET Core 2.0 Preview 你可能需要的前置知识1.了解如何通过Hyper-V安 ...

  3. Node.js快速入门

    Node.js是什么? Node.js是建立在谷歌Chrome的JavaScript引擎(V8引擎)的Web应用程序框架. 它的最新版本是:v0.12.7(在编写本教程时的版本).Node.js在官方 ...

  4. laravel 中CSS 预编译语言 Sass 快速入门教程

    CSS 预编译语言概述 CSS 作为一门样式语言,语法简单,易于上手,但是由于不具备常规编程语言提供的变量.函数.继承等机制,因此很容易写出大量没有逻辑.难以复用和扩展的代码,在日常开发使用中,如果没 ...

  5. npm 与 package.json 快速入门教程

    npm 与 package.json 快速入门教程 2017年08月02日 19:16:20 阅读数:33887 npm 是前端开发广泛使用的包管理工具,之前使用 Weex 时看了阮一峰前辈的文章了解 ...

  6. [转载]npm 与 package.json 快速入门教程

    npm 与 package.json 快速入门教程 2017-08-02 19:16:20 拭心 阅读数 78648更多 分类专栏: 学学前端   版权声明:本文为博主原创文章,遵循CC 4.0 BY ...

  7. 专为设计师而写的GitHub快速入门教程

    专为设计师而写的GitHub快速入门教程 来源: 伯乐在线 作者:Kevin Li     原文出处: Kevin Li 在互联网行业工作的想必都多多少少听说过GitHub的大名,除了是最大的开源项目 ...

  8. EntityFramework6 快速入门教程

    EntityFramework6 快速入门教程 不得不说EF在国内实在是太小众,相关的技术文章真实屈指可数,而且很多文章都很旧了,里面使用的版本跟如今的EF6差别还是比较大.我刚开始弄这个的时候真是绕 ...

  9. Apple Watch开发快速入门教程

     Apple Watch开发快速入门教程  试读下载地址:http://pan.baidu.com/s/1eQ8JdR0 介绍:苹果为Watch提供全新的开发框架WatchKit.本教程是国内第一本A ...

随机推荐

  1. JS原型链理解

    1. 每个对象都有原型属性(__proto__)2. 对象的原型(__proto__)指向其构造函数(Class)的prototype属性3. 构造函数(Class)的prototype属性本身也是一 ...

  2. C语言二维数组中的指针问题

    #include "stdio.h" void main() { int a[5][5]; int i,j; for (i=0;i<5;i++) { for (j=0;j&l ...

  3. openssh for windows安装

     openssh for windows安装 2009-11-22 22:43:58 分类: WINDOWS 本文转自:http://blog.chinaunix.net/uid-7541208-id ...

  4. Teradata SQL programming

    Teradata的SQL设计和Oracle真不是一个水平, 一点美感的没有.  上个世纪它靠着MPP一招鲜吃变天, 居然做了十多年数据仓库的老大,  时过境迁, 现在有不少SQL On Hadoop ...

  5. 全栈工程师是不是必须得会HTML5?

    最近看了很多招聘,都要求前端工程师必须得会HTML5.个人觉得只要会了html,HTML5不会太难,不过全栈工程师要求会的太多了 HTML CSS JavaScript HTML 5 CSS 3 都说 ...

  6. IBM:领导力素质的三环模式

    文章来源:http://blog.vsharing.com/sdtcj/A1826934.html   在翰威特公司于2003年评选的美国企业“领导人才最佳雇主”名单上,IBM名列榜首.而纽约< ...

  7. 滑雪 why WA

    滑雪 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 587  Solved: 219 Description 小明喜欢滑雪,因为滑雪的确很刺激,可是为了获 ...

  8. oracle删除表以及清理表空间

    若要彻底删除表,则使用语句:drop table <table_name> purge; 清除回收站里的信息 清除指定表:purge table <table_name>; 清 ...

  9. I’ve seen the world,lit it up as my stage now

    I've seen the world,lit it up as my stage now 阅尽繁华 点亮红尘做舞台 Channeling angels in,the new age now 粉末登场 ...

  10. .net的一些新语法的整理

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Tex ...