React 入门笔记
一、什么是React
React: A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES
上面的话直译过来就是,React是一个用于构建用户界面的JavaScript库。
React起源于Facebook内部项目,后来觉得不错,在2013年6月开源。
二、学习React
我学习的时候是基于 React-Demos 这个阮一峰博客上写的Demo库以及他的博客学习的,基本上跟着Demo跑一次下来就差不多入门了。前提是你对JavaScript和DOM有一定的了解。
三、安装
1.直接到官网下载
2.看着React-Demos学习的话,React-Demos中就自带了React源码,直接git clone或者下载zip压缩包
四、HelloWorld
下面是使用React网页的基本结构
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="helloworld"></div> <script type="text/jsx"> // your code React.render( <h1>Hello, World!</h1>, document.getElementById('helloworld') ); </script> </body> </html>
注意:
1.最后的script标签的type属性是“text/jsx”。因为React独有的JSX语法与JacaScript不兼容,使用JSX的地方都有显示表明type属性为"text/jsx"。
2.React提供的两个库react.js和JSXTransformer.js都必须首先加载。JSXTransformer.js的作用是件JSX语法转为JavaScript语法,这一步实际应该放到服务器完成,因为非常耗时间。
上面的代码中用到了React.render方法,这是React最基本的方法,用于将模板转为HTML语言并插入指定的DOM节点。
我们看到代码中,HTML语言直接写在JavaScript语言中,没有任何引号,这是JSX语法,允许HTML与JavaScript混写。
五、JSX语法
把上面的HelloWorld代码修改一下。
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="helloJSX"></div> <script type="text/jsx"> // your code var names = ['John', 'Amin', 'Ann']; React.render( <div> { names.map(function (name) { return <h1>Hello, {name}!</h1> }) } </div>, document.getElementById('helloJSX') ); </script> </body> </html>
上面这种像XML的语法叫JSX语法。
在JSX语法中,遇到HTML标签(以<开头)就用HTML规则解析,遇到代码块(以{开头)就用JavaScript规则解析。
上面代码运行出来就是id为helloworld的div下再嵌套着一个div,然后再嵌套三个h1标题。
JSX还允许在模板中直接插入JavaScript变量,变量是数组就展开数组成员。
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="helloArray"></div> <script type="text/jsx"> // your code var arr = [ <h1>Hello, world!</h1>, <h2>Hello, Amin!</h2>, <h3>React is awesome</h3> ]; React.render( <div>{arr}</div>, document.getElementById('helloArray') ); </script> </body> </html>
上面代码会在id为helloArray的div下嵌套三个套着div的标题。
我本来想看一下如果不想在标题外面再嵌套div要怎么做,暂时还没发现。
六、组件(component)
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="helloComponent"></div> <script type="text/jsx"> // your code var HelloMessage = React.createClass({ render: function() { return <h1>hello, {this.props.name}</h1>; } }); React.render( <HelloMessage name="John"/>, document.getElementById('helloComponent') ); </script> </body> </html>
React允许代码封装成组件,在网页中插入组件就像插入普通HTML标签一样。
React.createClass方法是用来生成一个组件类,上面的HelloMessage就是这个方法生成的一个组件类。在模板插入<HelloMessage />时和插入普通的HTML标签一样,但是会自动生成HelloMessage这个组件类的一个实例。在组件类中应该有自己的render方法来输出组件。
组件可以加入任意的属性,和HTML标签的用法一样。上面的HelloMessage组件中就有一个name属性。组件属性在组件类的this.props对象上可以获取。
添加组件属性的时候需要注意不要和JavaScript保留字冲突。如果要用到class和for应该这么写:
var MyComponent = React.createClass({ render: function() { return <div className="first"><span>A Span</span></div>; } });
this.props对象属性基本上和组件属性对应,除了this.props.children属性,它表示的意思是组件的所有子节点。
见下面代码
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <script type="text/jsx"> // your code var NoteList = React.createClass({ render: function() { return ( <ol> { this.props.children.map(function (child) { return <li>{child}</li>; }) } </ol> ); } }); React.render( <NoteList> <span>Hello, world!</span> <span>Hello, Amin!</span> <span>Hello, Ann!</span> </NoteList>, document.body ); </script> </body> </html>
NoteList组件类通过this.props.children读取NoteList组件实例下的三个span子节点,然后重新进行转化再插入到body中。
注意子节点必须多于1个,this.props.children才会是一个数组,当它是数组时才能用map方法,否则会报错。
我们需要知道,组件并不是真实的DOM节点,它是存在于内存中叫虚拟DOM(virtual DOM)的一种数据结构。组件插入文档中以后才会成为真实的DOM节点。
根据React的diff算法,DOM变动都先在虚拟DOM上发生,然后将发生变动部分反映到真实DOM上。
如果需要从组件中获取真实DOM节点,需要用到的方法叫React.findDOMNode.
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="example"></div> <script type="text/jsx"> // your code var MyComponent = React.createClass({ handleClick: function() { React.findDOMNode(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> ); } }); React.render( <MyComponent />, document.getElementById('example') ); </script> </body> </html>
我对上面代码的理解是,在组件MyComponent的子节点中有一个文本输入框,用于获取用户输入,onClick绑定的事件是组件中的handleClick,让文本框获得焦点。
这时就需要获得真实的DOM节点,为了做到这点,首先文本输入框要有一个ref属性,我对ref属性的理解是通过这个属性文本输入框被分配了一个名字,通过this.refs.[refName]就能指向这个虚拟DOM的子节点,最后通过React.findDOMNode方法获取真实DOM节点。
React把组件看成一个状态机,一开始有一个初始状态,组件与用户互动导致状态变化的话,会触发重新渲染UI。
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="state"></div> <script type="text/jsx"> // your code 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> ); } }); React.render( <LikeButton />, document.getElementById('state') ); </script> </body> </html>
上面的效果就是你点击div里面的文字会进行切换。
LikeButton组件中的getInitialState方法定义初始状态,这个对象通过this.state读取。每次点击都会导致状态变化,this.setState方法用于修改状态值,每次修改之后都会自动调用this.render方法,再次渲染组件。
当组件的特性是会随着与用户互动而产生变化时,我们应该使用this.state,而不是this.props