用TypeScipt和AMD模块化理念实现React官方教程(三)静态页面
现在我们可以象用C#开发那样来开发React应用了,我们的目的就是将 React官方教程整个迁移到typescript上实现:
创建第一个组件
在IDE的新建项中,我们可以找到TypeScript JSX文件,扩展名为tsx,如图:
在js目录下创建如下文件:
CommentBox.tsx
现在我们不需要象原教程那样命名.js文件了。完全可以按类来命名文件,这就是发挥TypeScript中Type 的力量的时候了!
文件内容如下:
/// <reference path="../typings/react/react-global.d.ts"/>
export =CommentBox;
class CommentBox extends React.Component<any, any> {
render() {
return <div className="commentBox">我是一个评论框。 </div>;
}
}
注意HTML元素我们用小写字母开头,而自定义的Ract类我们用大写字母开头。
另外创建如下文件作为主文件:
main.tsx
文件内容如下:
/// <reference path="../typings/react/react-global.d.ts"/>
import CommentBox = require("./CommentBox");
ReactDOM.render(<CommentBox />, document.getElementById('content'));
这时点按运行,不出意外的话,浏览器将显示正常结果,内容为:我是一个评论框。
组合组件
让我们跟随示例创建CommentList 和 CommentForm如下:
CommentList.tsx
文件内容如下:
/// <reference path="../typings/react/react-global.d.ts"/>
export =CommentList;
class CommentList extends React.Component<any, any> {
render() {
return <div className="commentList">你好,我是一个评论列表。</div>;
}
}
CommentForm.tsx
文件内容如下:
/// <reference path="../typings/react/react-global.d.ts"/>
export =CommentForm;
class CommentForm extends React.Component<any, any> {
render() {
return <div className="commentForm">你好,我是一个评论表单。</div>;
}
}
接下去修改CommentBox.tsx,如下:
/// <reference path="../typings/react/react-global.d.ts"/>
import CommentList = require("./CommentList");
import CommentForm = require("./CommentForm");
export =CommentBox;
class CommentBox extends React.Component<any, any> {
render() {
return (
<div className="commentBox">
<h1>评论</h1>
<CommentList />
<CommentForm />
</div>
);
}
}
使用props
现在让我们他建Comment组件,它将使用从父组件那里传入的数据。从父组件那里传入的数据将作为”属性(propery)”存在于子组件中。这些属性(properties)通过this.props访问。使用props,我们可以从CommentList传入的数据读取到Comment中,将生成一些标记:
Comment.tsx
文件内容如下:
/// <reference path="../typings/react/react-global.d.ts"/>
export =Comment;
class Comment extends React.Component<any, any> {
render() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{this.props.children}
</div>
);
}
}
组件属性
上面我们已经定义好了Comment
组件,我们想要将作者名字和评论文本传入。这样可以让我们为每个单独的评论重用想同的代码。现在让我们在CommentList
中添加一些评论:
CommentList.tsx
/// <reference path="../typings/react/react-global.d.ts"/>
import Comment = require('./Comment');
export =CommentList;
class CommentList extends React.Component<any, any> {
render() {
return (
<div className="commentList">
<Comment author="Eureka Chen">这是其中一个评论</Comment>
<Comment author="Jordan Walke">这是 *另一个* 评论</Comment>
</div>
);
}
}
添加Markdown解析
在index.html我们引用了第三方的marked库,将Markdown文件转化为Html。将comment.tsx改写成:
/// <reference path="../typings/react/react-global.d.ts"/>
/// <reference path="../typings/marked/marked.d.ts"/>
export =Comment;
class Comment extends React.Component<any, any> {
render() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{ marked(this.props.children.toString())}
</div>
);
}
}
注意在html中添加对maked.js的引用。
现在我们得到如图示的结果:
但是这里有个问题!从如图的输出中,直接输出了Html标签。我们需要他们生成Html。这是因为React防止XSS攻击,这里有个方法,但框架警告你不要这样。
/// <reference path="../typings/react/react-global.d.ts"/>
/// <reference path="../typings/marked/marked.d.ts"/>
export =Comment;
class Comment extends React.Component<any, any> {
rawMarkup() {
var rawMarkup = marked(this.props.children.toString(), { sanitize: true });
return { __html: rawMarkup };
}
render() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup() } />
</div>
);
}
}
这是API特意让原生HTML难以插入,但marked可以利用这个后门。
挂接到数据模型
到现在我们是直接在源代码中插入评论。现在,让我们使用JSON数据插入评论中。最终这些数据会来自服务器,但现在,我们写在源码中:
我们在main.tsx中加入data,如下:
/// <reference path="../typings/react/react-global.d.ts"/>
import CommentBox = require("./CommentBox");
var data = [
{ id: 1, author: "Eureka Chen", text: "这是一个评论" },
{ id: 2, author: "Jordan Walke", text: "这是 *另一个* 评论" }
];
ReactDOM.render(<CommentBox data={data} />, document.getElementById('content'));
然后在CommentBox.tsx中改为:
/// <reference path="../typings/react/react-global.d.ts"/>
import CommentList = require("./CommentList");
import CommentForm = require("./CommentForm");
export =CommentBox;
class CommentBox extends React.Component<any, any> {
render() {
return (
<div className="commentBox">
<h1>评论</h1>
<CommentList data={this.props.data} />
<CommentForm />
</div>
);
}
}
现在数据传到了CommentList,让我们动态生成评论,CommentList.tsx如下:
/// <reference path="../typings/react/react-global.d.ts"/>
import Comment = require('./Comment');
export =CommentList;
class CommentList extends React.Component<any, any> {
render() {
var commentNodes = this.props.data.map(
function (comment) {
return (
<Comment author={comment.author} key={comment.id}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
}
最终结果如下:
下一次我们开始讲从服务器获取数据。