HTML模板
<!DOCTYPE html>
<html>
<head>
<script src="../build/react.js"></script>
<script src="../build/react-dom.js"></script>
<script src="../build/browser.min.js"></script>
</head>
<body>
<div id="test"></div>
<script type="text/babel">
// 我们写代码的地方
</script>
</body>
</html>
这是一个最简单的HTML的模板,首先我们看一下我们在这个代码中引入了3个库:react.js
、react-dom.js
、browser.min.js
,其中react.js
是React的核心库,react-dom.js
提供了个DOM
相关的功能,browser.min.js
的作用是将JSX
的语法转换为JavaScript
的语法。因为React框架有独属于自己的JSX
语法,这个语法通常是和JavaScript
不兼容的,所以凡是JSX
语法的地方都要加上script type="text/babel"
标签。
ReactDOM.render()方法
ReactDOM.render()
方法是React
框架中最最最基础的方法,这个方法的作用是将模板转化为HTML
的语法,然后插入到指定的DOM
节点中。
<script type="text/babel">
ReactDOM.render(
<p>Hello, world!</p>,
document.getElementById('test')
);
</script>
上面的代码将一个<p>
插入到test
的节点中,其中document.getElementById('test')
指定被插入的节点。
JSX的语法
我们上面就在将JSX
语法,那么到底什么是JSX
语法呢,其实我们可以简单的认为将HTML语言直接写在JavaScript
语言之中,不加任何的引号,JSX
语法允许HTML
和JavaScript
的混写。
var name =['kim' , 'tom' , 'tony'];
ReactDOM.render(
<div>
{
name.map(function(name){
return <div> hello {name}!</div>
})
}
</div>,
document.getElementById('test');
);
总结一下:遇到HTML
标签,就使用HTML
的规则解析;遇到代码块,使用{}包裹的代码,就用JavaScript
规则进行解析。
当然JSX
允许直接在模板中插入JavaScript
变量,如果这个变量是一个数组的话,我们会展开这个数组的所有的成员。
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
会在结果中打出Hello world!,React is awesome两个标题。
组件
React框架允许我们将代码封装成组件,然后像插入普通的HTML
标签一样,在网页中插入这个组件。
var HelloMessage = React.creatClass({
render :function(){
return <h1>hello {this.props.name}</h1>
}
});
ReactDOM.render(
<HelloMessage name ='kim' />
document.getElement('test');
)
在上面的代码里面,HelloMessage
就是一个组件类,我们在模板中直接插入了<HelloMessage />
就会自动的生成HelloMessage
的实例,在React
中所有的组件都必须有自己的render
方法,这个方法的目的是输出组件。
ps:组件的第一个字母必须大写,否则就会报错,而且一个组件类智能包含一个顶级标签,否则也会报错。举个栗子:
var HelloMessage = React.createClass({
render: function() {
return <h1>
Hello {this.props.name}
</h1><p>
some text
</p>;
}
});
上面的代码就会报错,以为在一个组件类中包含了2个顶层标签,<p>
和<h1>
标签。组件的用法和原生的HTML
标签相比没有任何的区别,可以加入任意的属性,上例中我们就在HelloMessage
标签中加入了name
属性。其中组件的属性可以在组件类的this.props
对象上获取。在添加组件属性的时候,也需要注意class
要写成className
,for
方法要写成htmlFor
,因为class
和for
是JavaScript
的保留字。this的指向:this指向的是组件类。
this.props.children
this.props
对象的属性和组件的属性是一一对应的,但是还是有一个例外的,那就是this.props.children
实行。他表示组件所有的子节点。
var NotesList = React.createClass({
render: function() {
return (
<ol>
{
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>;
})
}
</ol>
);
}
});
ReactDOM.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,
document.body
);
实现的结果是:
ps:this.props.children
的值可能存在三种情况,第一种就是当前的组件没有子节点,那么this.props.children
返回的就是undefined
,如果只有一个节点的话,那么返回的数据类型就是Object
;如果有多个节点的话,返回的数据类型就是一个数组array
,所以处理this.props.children
的时候要格外的小心。
为了解决上述的问题,React
框架为我们提供了一个React.Children
的方法来处理this.props.children
。我们可以根据React.Children.map
来进行子节点的遍历。不用再担心React.Children
的数据类型究竟是undefined
还是object
。
PropTypes
组件的属性在一定的意义上有一点类似于函数的参数,所以组件的属性值可以接受任意值,字符串、对象、数组、函数等等都可以。所以我们现在需要一种机制来验证别人传过来的值是否符合要求。组件的PropTypes
属性,就可以用来检测组件的实例属性是否符合要求。
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
上面这段代码的意思就是在MyTitle
组件有一个title的属性。PropTypes
告诉React
,这个title
的属性是必须的,而且这个属性的数据类型是字符串,如果我们为title
的属性设置为number
类型的话,验证就无法通过,会在控制台输出一行错误信息。
还有一个关于属性的就是getDefaultProps
方法可以用来设置组件属性的默认值。
var MyTitle = React.createClass({
getDefaultProps : function () {
return {
title : 'Hello World'
};
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
ReactDOM.render(
<MyTitle />,
document.body
);
这个方法的意思就是在没有显示的对title
的值就行定义的话,我们就直接使用默认值,也就是说上面的代码默认显示的是‘Hello World’。
获取真实的DOM节点
我们在写的时候讲组件当做真正的HTML
的语法来写,但是其实组件并不是真正的DOM
节点,而是存在于内存之后的一种数据机构,也就是虚拟DOM
,只有将它插入到文档之后,它才会变成真正的DOM
,在React
设计的本意是,所有的DOM
的变动是先发生在虚拟DOM
中的,然后再将实际发生变动的部分,反映在真实的DOM
中。
但是,有时候,我们需要从组件中获取真实的DOM
,这个时候就要用到ref
属性了。
var MyComponent = React.createClass({
handleClick: function() {
this.refs.myTextInput.focus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<input type="button" value="Focus the text input" onClick={this.handleClick} />
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('test')
);
组件MyComponent
的子节点有一个文本输入框,来得到用户的输入,现在这个情况就是必须获取真实的DOM
节点,虚拟DOM
对于用户来说是拿不到的,所以我们为了可以获取到这个值,文本框必须有一个ref
属性,然后在使用this.refs.[refName]
就可以返回这个真实DOM
节点。由于this.refs.[refName]
属性获取的是真正的DOM
,所以一定要等到虚拟的DOM
插入文档以后,才可以使用这个属性,要不然就会报错。在上面代码中指定了click
回调函数,确保了DOM
获取发生在Click
之后,才会读取this.refs.[refName]
属性。重点就是一定要在虚拟DOM
插入到文档之后再进行操作。
this.state
组件是无法避免的要与用户进行互动,我们可以将组件看做是一个状态机,一开始有一个初始状态,然后与用户进行互动,不断的导致状态的改变,从而触发重新的渲染UI
。
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});
ReactDOM.render(
<LikeButton />,
document.getElementById('test')
);
解释一下上面的代码:LikeButton
这个组件,他有一个getInitialState
方法定义了初始状态,这也是一个对象,这个对象可以通过this.state
属性读取。当用户点击组件的时候,状态发生了改变,this.setState
方法用来进行状态的修改。每一次状态发生改变,就会自动的调用this.render
方法,再次进行组件的渲染。
this.props
表示哪些属性一旦被定义,就不能被修改;而this.state
会随着用户的互动而大声变化的特性。
表单
用户在填写表单的时候,属于用户和组件之间的互动,所以不能用this.props
来进行读取。
var Input = React.createClass({
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function () {
var value = this.state.value;
return (
<div>
<input type="text" value={value} onChange={this.handleChange} />
<p>{value}</p>
</div>
);
}
});
ReactDOM.render(<Input/>, document.body);
文本框中的值,不能用this.props.value
来进行读取,而要定义一个onchange
事件的回调函数。通过 event.target.value来读取用户输入的值。
组件的生命周期
组件的声明周期主要分为3个状态:
Mounting:这个是已经插入的DOM
Updating:正在被重新渲染
Unmounting:已移除的真实DOM
React 为每个状态都提供了两种处理函数,will
函数在进入状态之前调用,did
函数在进入状态之后调用,三种状态共计五种处理函数。
- componentWillMount()
- componentDidMount()
- componentWillUpdate(object nextProps, object nextState)
- componentDidUpdate(object prevProps, object prevState)
- componentWillUnmount()
React 还提供两种特殊状态的处理函数。
- componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
- shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
var Hello = React.createClass({
getInitialState: function () {
return {
opacity: 1.0
};
},
componentDidMount: function () {
this.timer = setInterval(function () {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
},
render: function () {
return (
<div style={{opacity: this.state.opacity}}>
Hello {this.props.name}
</div>
);
}
});
ReactDOM.render(
<Hello name="world"/>,
document.body
);
did函数在进入状态之后调用,我们在这里调用了componentDidMount的方法,也就是在hello组件加载之后,然后在componentDidMount方法中设置一个定时器,这个定时器的目的是每隔100毫秒,但是在透明图小于一定值的话,就会重新的渲染。
我们注意到我们对样式的定义是style={{opacity: this.state.opacity}}
,而不是style="opacity:{this.state.opacity};"
,因为react的组件的样式实质上是一个对象,所以第一重大括号表示的是js的语法,第二重大括号表示的是对象。
Ajax
对于组件的数据来源,通常是通过Ajax请求在服务器获取的,我们依旧可以通过componentDidMount
方法来设置Ajax请求,等到请求成功的时候,再使用this.setState方法重新渲染UI。
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
},
render: function() {
return (
<div>
{this.state.username}'s last gist is
<a href={this.state.lastGistUrl}>here</a>.
</div>
);
}
});
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
document.body
);
上面的代码的实现是使用jquery来完成Ajax的请求,这也就很方便的说明了。react本身是没有任何的依赖的,我们可以不使用jquery而是使用其他的库。
我们现在比较经常使用的是使用promise来进行操作,而且我们通过查看promise的状态来返回了不同的信息。
var RepoList = React.createClass({
getInitialState: function() {
return { loading: true, error: null, data: null};
},
componentDidMount() {
this.props.promise.then(
value => this.setState({loading: false, data: value}),
error => this.setState({loading: false, error: error}));
},
render: function() {
if (this.state.loading) {
return <span>Loading...</span>;
}
else if (this.state.error !== null) {
return <span>Error: {this.state.error.message}</span>;
}
else {
var repos = this.state.data.items;
var repoList = repos.map(function (repo) {
return (
<li>
<a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}
</li>
);
});
return (
<main>
<h1>Most Popular JavaScript Projects in Github</h1>
<ol>{repoList}</ol>
</main>
);
}
}
});