React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
ReactJS官网地址:http://facebook.github.io/react/
Github地址:https://github.com/facebook/react
React认为一个组件应该具有如下特征:
(1)可组合(Composeable):一个组件易于和其它组件一起使用,或者嵌套在另一个组件内部。如果一个组件内部创建了另一个组件,那么说父组件拥有(own)它创建的子组件,通过这个特性,一个复杂的UI可以拆分成多个简单的UI组件;
(2)可重用(Reusable):每个组件都是具有独立功能的,它可以被使用在多个UI场景;
(3)可维护(Maintainable):每个小的组件仅仅包含自身的逻辑,更容易被理解和维护;
我们新建一个html文件,引用react.js和JSXTransformer.js这两个js文件。html模板如下(js路径改成自己的):
<!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="example"></div> <script type="text/babel"> // ** Our code goes here! ** </script> </body></html>
script的type是text/babel,这是因为 React 独有的 JSX 语法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel" 。
其次,上面代码一共用了三个库: react.js 、react-dom.js 和 Browser.js ,它们必须首先加载。其中,react.js 是 React 的核心库,react-dom.js 是提供与 DOM 相关的功能,Browser.js 的作用是将 JSX 语法转为 JavaScript 语法,这一步很消耗时间,实际上线的时候,应该将它放到服务器完成。下面开始看react该怎么写,还是传统的老规矩 hello word:
<!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="example"></div> <script type="text/babel"> ReactDOM.render(<h1>hello word</h1>,document.getElementById('example')); </script> </body> </html>
在上例的body中,有一个div,id是example, 然后由react的渲染器把h1标签渲染到该div中。
ReactDOM.render(DOM节点,被插入的父节点);
这一句代码是最常用也是非常必要的一句。 这句中有个很重要的地方就是html语法和js语法的混写,这是jsx语法的特点,在jsx语法里,遇到 尖括号就按照html来解析,遇到花括号就按照js语法去解析。下面这个例子更能说明jsx语法的写法:
var list = ["word","man","boy"]; ReactDOM.render( <div> { list.map(function (name){ return <div> hello {name}</div> }) } </div>,document.getElementById('example')); </script>
最终的结果是:
hello word
hello man
hello boy
这里有一点需要说明的是 ReactDOM.render 渲染器的第一个参数里,有且只能有一个顶级节点,不能有多个并列,否则会报错。JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员。
var list = [<h1>word</h1>,<h1>man</h1>,<h1>boy</h1>]; ReactDOM.render(<div>{list}</div>,document.getElementById('example')); </script>
最终结果是:
React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类:
var HelloWord = React.createClass({ render:function(){ return <p>hello {this.props.name}</p> } }); ReactDOM.render(<HelloWord name="喵星人"/>,document.getElementById('example'));
最终结果是:
hello 喵星人
上例中用React.createClass创建了一个组件类HelloWord,这里有一点需要注意:组件类名字的首字母必须是大写的,否则会报错。React.createClass接收一个对象,对象中必须有render属性,render属性的值一般都是写成一个函数,然后返回一个DOM对象,别忘了上面说的,这个DOM对象有且只能有一个顶级节点,否则会报错。
我们在上面的代码的return 里会看到有这么一段:this.props.name,然后在ReactDom.render中的第一个参数里会看到标签有一个属性name="喵星人",这里需要记住的一点是name="喵星人"看上去就像在对函数传参数,而this.props.name就是对参数的接收。
添加组件属性,有一个地方需要注意,就是 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。
this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点。
var NotesList = React.createClass({ render: function() { return ( <ol> { this.props.children.map(function (child) { return <li>{child}</li>; }) } </ol> ); }});ReactDOM.render( <NotesList> <span>hello</span> <span>world</span> <p>main</p> </NotesList>, document.getElementById('example'));
最终运行结果:
hello
world
3. main
2 和3之间的换行是因为p标签会自带一个换行,li标签也会有一个换行,所以中间就有了一个空白行.
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('example'));
最终结果:
You haven't liked this. Click to toggle.
当在文字上点击时会变成:
You like this. Click to toggle.
然后一直点击就会在两句英文之间来回切换。
这里需要注意的是 getInitialState 是React默认的属性,需要返回一个对象,可以是NULL或者其他,表示初始状态,返回的对象中可以不止有一个属性,修改这些状态可以使用react提供的方法this.setState(对象), 这里的对象可以只修改初始状态中的某一个属性,也可以同时修改多个。当任何一个状态改变的时候react就会重新调用 render属性所指向的函数,也就意味着会重新渲染该组件。
组件的生命周期分成三个状态:
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);
上面代码在hello组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒,就重新设置组件的透明度,从而引发重新渲染。这里需要注意的地方是在setInterval的第一个参数也就是计时器触发函数后使用了bind(this),计时器函数中不能直接使用this来获取react的状态或属性,需要绑定this。
小结:
react提供的属性:
getInitialState 设置默认状态
setState 修改状态
render 返回组件
componentWillMount() 插入真实DOM之前调用
componentDidMount() 插入真实DOM之后调用
componentWillUpdate(object nextProps, object nextState) 重新渲染之前调用
componentDidUpdate(object prevProps, object prevState) 重新渲染之后调用
componentWillUnmount() 移出真实DOM之前调用
propTypes 验证组件实例的属性是否符合要求
getDefaultProps 设置组件属性的默认值。
最后两个属性本文中没有介绍,详细介绍请参阅http://www.ruanyifeng.com/blog/2015/03/react.html