gistfile1.txt create-react-js # Installation: You’ll need to have Node = 6 on your machine. You can use nvm to easily switch Node versions between different projects.npm install -g create-react-app# Creating an Appcreate-react-app my-ap
create-react-js # Installation: You’ll need to have Node >= 6 on your machine. You can use nvm to easily switch Node versions between different projects. npm install -g create-react-app # Creating an App create-react-app my-app cd my-app It will create a directory called my-app inside the current folder. Inside that directory, it will generate the initial project structure and install the transitive dependencies: my-app ├── README.md ├── node_modules ├── package.json ├── .gitignore ├── public │ └── favicon.ico │ └── index.html │ └── manifest.json └── src └── App.css └── App.js └── App.test.js └── index.css └── index.js └── logo.svg └── registerServiceWorker.js npm start or yarn start npm test or yarn test # Runs the test watcher in an interactive mode. By default, runs tests related to files changed since the last commit npm run build or yarn build # Builds the app for production to the build folder.It correctly bundles React in production mode and optimizes the build for the best performance. state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。 state 中状态可以通过 this.setState 方法进行更新,setState 会导致组件的重新渲染。 props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。 但是它们的职责其实非常明晰分明:state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。 没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。 import React,{ Component } from 'react' import CommentInput from './CommentInput' import CommentList from './CommentList' class CommentApp extends Component{ //似乎只能靠console打印了 handleSubmitComment(content){ console.log(content) } render(){ return(# 注意下这行 ) } } export default CommentApp import React,{Component} from 'react' class CommentInput extends Component{ constructor(){ super() this.state = { username:'', content:'' } } handleUsernameChange(event){ this.setState({ username:event.target.value }) } handleContentChange(event){ this.setState({ content:event.target.value }) } handleSubmitButton(event){ if (this.props.onSubmit){ const {username,content} = this.state this.props.onSubmit({username,content}) } this.setState({content:''}) } render(){ return( 用户名: 评论内容: ) } } export default CommentInput import React,{Component} from 'react' class Comment extends Component{ render(){ return( {this.props.comment.username} :{this.props.comment.content}
) } } export default Comment /** * Created by Administrator on 2017/11/8. */ import React,{Component} from 'react' import Comment from './comment' class CommentList extends Component{ render(){ const comments = [ {username: 'Jerry', content: 'Hello'}, {username: 'Tomy', content: 'World'}, {username: 'Lucy', content: 'Good'} ] return( // { // comments.map((comment,i)=>{ // return( // {comment.username}:{comment.content} // ) // }) // } { comments.map((comment,i)=>) } ) } } export default CommentList /** * Created by Administrator on 2017/11/8. */ import React,{ Component } from 'react' import CommentInput from './CommentInput' import CommentList from './CommentList' class CommentApp extends Component{ //s似乎只能靠console打印了 constructor(){ super() this.state={ comments:[] } } handleSubmitComment(content){ console.log(content) this.state.comments.push(content) this.setState({ //更新还是必须这个操作的 comments:this.state.comments }) } render(){ return() } } export default CommentApp 挂载阶段的组件生命周期 -> constructor() -> componentWillMount() -> render() // 然后构造 DOM 元素插入页面 -> componentDidMount() // ... // 从页面中删除 class Header extends Component{ constructor(){ super() console.log('constructor') } componentWillMount(){ console.log('将要挂载') } componentDidMount(){ console.log("已经挂载") } render(){ return( React小书
) } } React通过this.state来访问state,通过this.setState()方法来更新state。当this.setState()方法被调用的时候,React会重新调用render方法来重新渲染UI setState方法通过一个队列机制实现state更新,当执行setState的时候,会将需要更新的state合并之后放入状态队列,而不会立即更新this.state(可以和浏览器的事件队列类比)。如果我们不使用setState而是使用this.state.key来修改,将不会触发组件的re-render。如果将this.state赋值给一个新的对象引用,那么其他不在对象上的state将不会被放入状态队列中,当下次调用setState并对状态队列进行合并时,直接造成了state丢失。 void setState( function|object nextState, [function callback] ) 第二个参数是一个回调函数,在setState的异步操作结束并且组件已经重新渲染的时候执行。也就是说,我们可以通过这个回调来拿到更新的state的值。 React也正是利用状态队列机制实现了setState的异步更新,避免频繁地重复更新state(pending的意思是未定的,即将发生的) this.setState 是在 render 时, state 才会改变调用的, 也就是说, setState 是异步的. 组件在还没有渲染之前, this.setState 还没有被调用.这么做的目的是为了提升性能, 在批量执行 State 转变时让 DOM 渲染更快. ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。 ReactDOM.render(Hello, world!
, document.getElementById('example') ); 上面代码将一个 h1 标题,插入 example 节点(查看 demo01),运行结果如下。 JSX 语法 var names = ['Alice', 'Emily', 'Kate']; ReactDOM.render( { names.map(function (name) { return Hello, {name}! }) } , document.getElementById('example') ); 上面代码体现了 JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。上面代码的运行结果如下。 组件 React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类(查看 demo04)。 var HelloMessage = React.createClass({ render: function() { returnHello {this.props.name}
; } }); ReactDOM.render(, document.getElementById('example') ); 上面代码中,变量 HelloMessage 就是一个组件类。 模板插入 时,会自动生成 HelloMessage 的一个实例(下文的"组件"都指组件类的实例)。 所有组件类都必须有自己的 render 方法,用于输出组件。 注意,组件类的第一个字母必须大写,否则会报错,比如HelloMessage不能写成helloMessage。另外,组件类只能包含一个顶层标签,否则也会报错。 组件的用法与原生的 HTML 标签完全一致,可以任意加入属性,比如 ,就是 HelloMessage 组件加入一个 name 属性,值为 John。 组件的属性可以在组件类的 this.props 对象上获取,比如 name 属性就可以通过 this.props.name 读取。 添加组件属性,有一个地方需要注意,就是 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。 this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点 var NotesList = React.createClass({ render: function() { return ( { React.Children.map(this.props.children, function (child) { return
); } }); ReactDOM.render(- {child}
; }) }hello world , document.body ); 这里需要注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。 React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。更多的 React.Children 的方法,请参考官方文档。 组件的属性可以接受任意值,字符串、对象、函数等等都可以。有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。 组件类的PropTypes属性,就是用来验证组件实例的属性是否符合要求。 var MyTitle = React.createClass({ propTypes: { title: React.PropTypes.string.isRequired, }, render: function() { return{this.props.title}
; } }); 上面的Mytitle组件有一个title属性。PropTypes 告诉 React,这个 title 属性是必须的,而且它的值必须是字符串。现在,我们设置 title 属性的值是一个数值。 var data = 123; ReactDOM.render(, document.body ); 这样一来,title属性就通不过验证了。控制台会显示一行错误信息。 更多的PropTypes设置,可以查看官方文档。 此外,getDefaultProps 方法可以用来设置组件属性的默认值。 var MyTitle = React.createClass({ getDefaultProps : function () { return { title : 'Hello World' }; }, render: function() { return {this.props.title}
; } }); ReactDOM.render(, document.body ); 上面代码会输出"Hello World"。 七、获取真实的DOM节点 组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。 但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性 var MyComponent = React.createClass({ handleClick: function() { this.refs.myTextInput.focus(); }, render: function() { return ( ); } }); ReactDOM.render( , document.getElementById('example') ); 上面代码中,组件 MyComponent 的子节点有一个文本输入框,用于获取用户的输入。这时就必须获取真实的 DOM 节点,虚拟 DOM 是拿不到用户输入的。为了做到这一点,文本输入框必须有一个 ref 属性,然后 this.refs.[refName] 就会返回这个真实的 DOM 节点。 需要注意的是,由于 this.refs.[refName] 属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取 this.refs.[refName] 属性。 React 组件支持很多事件,除了 Click 事件以外,还有 KeyDown 、Copy、Scroll 等。 八、this.state 组件免不了要与用户互动,React 的一大创新,就是将组件看成是一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 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 ( You {text} this. Click to toggle.
); } }); ReactDOM.render(, document.getElementById('example') ); 上面代码是一个 LikeButton 组件,它的 getInitialState 方法用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取。当用户点击组件,导致状态变化,this.setState 方法就修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。 由于 this.props 和 this.state 都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,this.props 表示那些一旦定义,就不再改变的特性,而 this.state 是会随着用户互动而产生变化的特性。 十、组件的生命周期 组件的生命周期分成三个状态: 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 ( Hello {this.props.name} ); } }); ReactDOM.render( , document.body ); 上面代码在hello组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒,就重新设置组件的透明度,从而引发重新渲染。 另外,组件的style属性的设置方式也值得注意,不能写成 style="opacity:{this.state.opacity};" 而要写成 style={{opacity: this.state.opacity}} # 这是因为 React 组件样式是一个对象,所以第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象。