React JS快速入门教程
翻译至官方文档《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快速入门教程的更多相关文章
- .NET Core快速入门教程 2、我的第一个.NET Core App(Windows篇)
一.前言 本篇开发环境?1.操作系统: Windows 10 X642.SDK: .NET Core 2.0 Preview 二.安装 .NET Core SDK 1.下载 .NET Core下载地址 ...
- .NET Core快速入门教程 3、我的第一个.NET Core App (CentOS篇)
一.前言 本篇开发环境?1.操作系统:CentOS7(因为ken比较偏爱CentOS7)2.SDK版本:.NET Core 2.0 Preview 你可能需要的前置知识1.了解如何通过Hyper-V安 ...
- Node.js快速入门
Node.js是什么? Node.js是建立在谷歌Chrome的JavaScript引擎(V8引擎)的Web应用程序框架. 它的最新版本是:v0.12.7(在编写本教程时的版本).Node.js在官方 ...
- laravel 中CSS 预编译语言 Sass 快速入门教程
CSS 预编译语言概述 CSS 作为一门样式语言,语法简单,易于上手,但是由于不具备常规编程语言提供的变量.函数.继承等机制,因此很容易写出大量没有逻辑.难以复用和扩展的代码,在日常开发使用中,如果没 ...
- npm 与 package.json 快速入门教程
npm 与 package.json 快速入门教程 2017年08月02日 19:16:20 阅读数:33887 npm 是前端开发广泛使用的包管理工具,之前使用 Weex 时看了阮一峰前辈的文章了解 ...
- [转载]npm 与 package.json 快速入门教程
npm 与 package.json 快速入门教程 2017-08-02 19:16:20 拭心 阅读数 78648更多 分类专栏: 学学前端 版权声明:本文为博主原创文章,遵循CC 4.0 BY ...
- 专为设计师而写的GitHub快速入门教程
专为设计师而写的GitHub快速入门教程 来源: 伯乐在线 作者:Kevin Li 原文出处: Kevin Li 在互联网行业工作的想必都多多少少听说过GitHub的大名,除了是最大的开源项目 ...
- EntityFramework6 快速入门教程
EntityFramework6 快速入门教程 不得不说EF在国内实在是太小众,相关的技术文章真实屈指可数,而且很多文章都很旧了,里面使用的版本跟如今的EF6差别还是比较大.我刚开始弄这个的时候真是绕 ...
- Apple Watch开发快速入门教程
Apple Watch开发快速入门教程 试读下载地址:http://pan.baidu.com/s/1eQ8JdR0 介绍:苹果为Watch提供全新的开发框架WatchKit.本教程是国内第一本A ...
随机推荐
- Yii2.0中文开发向导——Yii2中多表关联查询(join、joinwith)(转)
我们用实例来说明这一部分 表结构 现在有客户表.订单表.图书表.作者表, 客户表Customer (id customer_name) 订单表Order (id order_ ...
- C# Thread挂起线程和恢复线程
前言 众所周知,Thread类中的挂起线程和恢复线程微软已标记过时,因为可能会造成问题 Resume() 恢复当前线程 已过时. Resumes a thread that has been ...
- [MySQL FAQ]系列 — 为什么InnoDB表要建议用自增列做主键
我们先了解下InnoDB引擎表的一些关键特征: InnoDB引擎表是基于B+树的索引组织表(IOT): 每个表都需要有一个聚集索引(clustered index): 所有的行记录都存储在B+树的叶子 ...
- linux下google chrome浏览器字体修改
今天安装了最新的chrome,我是下载的.deb包直接安装的. 安装完后,用chrome浏览页面时,发现字体有的大,有的小,还不清楚. 于是在网上搜索了一下如何设置字体. 1.打开Chrome浏览器. ...
- 基本select语句的生命周期
(1) 客户端sqlserver网络接口通过一种网络协议(可以是共享内存:简单高速,客户端和sql server在同一台计算机默认连接方式:TCP/IP:访问sql server最常用的一种协议,客户 ...
- Android应用如何监听自己是否被卸载及卸载反馈功能的实现
一个应用被用户卸载肯定是有理由的,而开发者却未必能得知这一重要的理由,毕竟用户很少会主动反馈建议,多半就是用得不爽就卸,如果能在被卸载后获取到用户的一些反馈,那对开发者进一步改进应用是非常有利的.目前 ...
- 2012多校3.A(用O(log(n))判断b^k % a == 0)
Arcane Numbers 1 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Su ...
- jQuery的$.ajax示例
$.ajax({ url: 'index.php?module=products&submod=product_experience_manage&method=ajaxGetSele ...
- springMVC之国际化
1.工程结构 2.jar包 3.配置文件spring-config.xml,springMVC配置文件 <?xml version="1.0" encoding=" ...
- HDU 5228
#include<stdio.h> #include<string.h> *]; int main(){ int t; scanf("%d",&t) ...