翻译至官方文档《Tutorial》http://facebook.github.io/react/docs/tutorial.htmljavascript
转载请注明出处:http://blog.csdn.net/adousenhtml
推荐阅读 React|RakNet 博客:http://blog.csdn.net/rsspub/article/category/1435601前端
教程演示样例代码,Web程序框架採用的是全栈python web框架Uliwebjava
Uliweb https://github.com/adousen/reactjs_uliweb_examplepython
在新手教程里,咱们会建立一个简单却有用的评论盒子来做为咱们的样例,你可以把它放进一个博客什么的。react
它实际上就是Disqus、LiveFyre、Facebook等实时评论的基础实现。
咱们要实现的功能有:jquery
此外,另外一些优化特性:git
在教程中。咱们直接使用的是CDN上的Javascript框架文件。
如下,打开随意你喜欢的编辑器。建立一个新的HTML文档:github
<!-- 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代码。web
注意:
此处咱们将jQuery包括了进来,但目的仅仅是为了方便编写ajax调用。但这不是在React中所必须作。
React所有的一切都是关于模块化、复合化的组件。就咱们的评论功能来讲。咱们将依照如下的组件结构来实现:
- CommentBox - CommentList - Comment - CommentForm
咱们先来建立一个CommentBox
组件,它一開始仅仅是一个简单的<div>
:
// tutorial1.jsvar CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> Hello, world! I am a CommentBox. </div> ); }});React.render( <CommentBox />, document.getElementById('content'));
首先。你注意到的是Javascript代码中的XML化语法。
咱们实际上可以使用一个预编译器来将此语法糖转换为纯Javascript:
// tutorial1-raw.jsvar 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.jsvar 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.jsvar 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.jsvar 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的缩写)。
接下来,咱们就来建立这个Comment组件。使用porps咱们可以读取从 CommentList
传递的数据,并渲染一些标记。
// tutorial5.jsvar 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
中的keywordprops是传递给组件的命名属性。
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.jsvar 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.jsvar 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数据。终于,咱们将从server端获取。但是现在,咱们把它直接写在代码中:
// tutorial8.jsvar 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.jsvar 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.jsvar 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> ); }});
就是这样!
如下,咱们用从server端读取的动态数据来替换硬性编码的数据。咱们删除了 data
属性,改成採用 URL
来获取:
// tutorial11.jsReact.render( <CommentBox url="comments.json" />, document.getElementById('content'));
这个组件和以前的组件的不一样之处在于它必须预先自行渲染。
在从server端得到请求应答以前,它没有可用的数据,而这些数据是组件呈现评论所必须的。
到现在为止,所有的组件都仅仅是依据自身的props进行一次性的渲染。props
是不可变的:它们是从父组件传递过来,并且为父组件所有。为了实现交互。咱们为组件引入了可变的 state 。this.state
属于组件的私有成员。并且可以经过调用 this.setState()
进行改动。当state更新以后。组件会立刻对其自身进行又一次渲染。
实际上在React代码中。render()
方法被被声明为 this.props
和 this.state
的函数,并由框架保证了UI老是与输入保持一致。
当从server取得数据后,就可以对咱们的评论数据进行改动。
首先,咱们向 CommentBox
组件的state加入一个包括评论数据的数组data:
// tutorial12.jsvar 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进行初始化。
在组件建立完成后,咱们还想要从serverGET到JSON,从而更新state来反映最新的数据。
在实际的应用中。咱们可能建立的是一个动态的应用。但是,在样例中为了简单。仍是使用一个静态的JSON文件:
// tutorial13.json[ {"author": "Pete Hunt", "text": "This is one comment"}, {"author": "Jordan Walke", "text": "This is *another* comment"}]
咱们打算使用jQuery对server进行异步的訪问。
注意: 由于这是一个AJAX应用,所以你需要在一个webserver上执行,而不能仍停留在文件系统。
最简单的方式是在应用的文件夹下执行python -m SimpleHTTPServer
。
// tutorial13.jsvar 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
的调用。在从server取得数据之后。咱们就使用新数组替换评论组件的旧数据,并且让它动态地改变。这样的反应的方式。使得动态更新仅仅是作了小小的改变。
此处,咱们使用的投票数据很是简单,你也可以很是easy使用WebSockets或其余技术来得到。
// tutorial14.jsvar 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
组件需要向询问用户他们的名字和评论的内容。并将其发送给server进行保存。
// tutorial15.jsvar 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提交之后,咱们需要将表单清空。并将一个请求发送到server。而后更新评论列表。那么,首先咱们需要监听表单的submit事件。并将其清空。
// tutorial16.jsvar 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> ); }});
React向组件加入的事件处理函数使用的是驼峰命名规则。
咱们向表单加入了一个 onSumbit
的处理函数。它负责在输入数据合法的表单提交后,将表单的字段清空。
在事件处理中。调用 preventDefault
是为了阻止浏览器默认的与表单提交有关的行为。
咱们使用 ref
属性向子组件分配了一个名字,并且经过 this.refs
对组件进行引用。咱们可以在一个组件上调用 getDOMNode
获取一个原生DOM元素。
当用户提交一条评论时,咱们还需要对以前的评论列表进行更新。让新的评论显示进来。对于含有与呈现评论有关数据的state的CommentBox
来讲,需要定义这种行为逻辑。
咱们需要从子组件传送数据到它的父组件。咱们在父组件的 render
方法中将一个新的回调函数(handleCommentSubmit
)传递给子组件,并将其绑定在子组件的 onCommentSubmit
事件上。当事件被触发后。回调函数就会被运行。
// tutorial17.jsvar 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.jsvar 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> ); }});
现在。回调函数已经定义完成。
咱们要作的就是提交新的评论到server,并刷新评论列表。
// tutorial19.jsvar 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> ); }});
现状咱们已经实现了这个应用的所有功能。
但是。在从server完毕请求以前,你必须等待评论在列表中出现。所以,会感受有点慢。咱们可以对它再作一点优化,让它感受更快一点。
// tutorial20.jsvar 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> ); }});
经过一些简单的步骤,你已成功建立了一个评论盒子。