React.js 学习总结
1.用React的优越性
独立,小巧,快速
ReactDOM 会构建一个虚拟的DOM tree, 只有DOM tree 的某个部分发生变化,便重新渲染某个部分。
React 判断DOM 是否重新渲染的依据就是:先DOM node 属性(key,other属性)再层级往上,直到root节点,再render发生改变的部分。
思考:
<li> apple</li>
<li>pear</li>
假定现在需要插入<li>oranage</li>到顶部
<li>oranage</li>
<li> apple</li>
<li>pear</li>
DOM 会重新渲染三个<li>标签
而如果是插入到底部:
<li> apple</li>
<li>pear</li>
<li>oranage</li>
这样DOM会发现虚拟tree中多了一个节点,只会render 插入新节点
因此我们在开发过程中,应注意根节点DOM的稳定,还有利用节点属性,如key(可以是自己store中的数据值),例如:
<li key=“1001”>oranage</li>
<li key=“1002”> apple</li>
<li key=“1003”>pear</li>
即使是插入顶部的时候,React从虚拟DOM tree中比较,发现节点key值的变化,下面的DOM只是发生位置的移动,而只渲染<li key=“1001”>oranage</li>标签。
当 old DOM destroyed时,会调用componentWillUnmount()方法
当 new DOM building时,调用componentWillMount() and componentDidMount() 方法
2.React 组件
UI界面开发,需要合理对组件进行分解,可按照功能,样式进行划分。
React Component应注意以下几点:
- 组件类的第一个字母必须大写,否则会报错,比如 HelloMessage不能写成 helloMessage。因为JSX解析” <HelloMessage .../>” 时,React会当组件来渲染,JSX解析<helloMessage../>时会当html元素渲染。
- 组件类只能包含一个顶层标签,否则也会报错。
- 所有组件类都必须有自己的 render 方法,用于输出组件。
- 不能用一般的表达式表示组件,可将其赋给一个大写的变量,作为组件使用。(同一)
const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { // Wrong! JSX type can't be an expression. return <components[props.storyType] story={props.story} />; }
修改为:
function Story(props) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}
定义组件,可以用ES6语法,也可以脱离ES6。写法上有一些差异。
class MyComponent extends React.Component { constructor(props){ super(props); this.handleClick = this.handleClick.bind(this); this.state = {message: “Hello!”}; } handleClick = () => { alert(this.state.message); } render() { // Because `this.handleClick` is bound, we can use it as an event handler. return ( <button onClick={this.handleClick}> Say hello </button> ); } } MyComponent.defaultProps = { name: 'Mary' }; var MyComponent = React.createClass({ propTypes:{ name : React.PropTypes.string.isRequired }, getDefaultProps:function(){ return { name: 'Mary' }; } getInitialState: function() { return {message: 'Hello!'}; }, handleClick: function() { alert(this.state.message); }, render: function() { return ( <button onClick={this.handleClick}> Say hello </button> ); } });
If you'd rather play it safe, you have a few options:
- Bind methods in the constructor.
- Use arrow functions, e.g. onClick={(e) => this.handleClick(e)}.
- Keep using createReactClass.
还有个方便的小地方:
- 用Es6 {foo} (= {foo: foo}) goober than {foo: true}
- 如果props是一个object, 可以用{…props} 来传递整个props。
- React 中 {false, null , undefined, true } 都不会渲染 等价于<div></div> , 如果需要以字符串的形式来显示 {String(false/null/undefined/true)}
- 0 会渲染,因此在实际中应用时一定加上”> 0”,如:if(w.length > 0){ …render}
3.JSX介绍
JSX其实就是语法糖,就是可以将html语言直接写在js中,不加任何引号. 在遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。
JSX允许直接在模板插入 JavaScript 变量(如果这个变量是一个数组,则会展开这个数组的所有成员),也可以插入React component,计算表达式等,出现在组件的render方法中。
class Li extends React.Component { return ( <li data-id={this.props.key}> {this.props.name} </li> ); } class UserList extends React.Component { constructor(props){ super(props); }
render(){
return (
<ul>
{this.props.userList.map((user) => {<Li key={user.id} name={user.name}></Li>})} //变量,表达式,组件...
</ul>
)
}
}
4.props 应用
React组件的继承,可以用props实现,它是父组件与子组件属性的传递纽带,是只读属性(Only-read),可以传递primitive value,React elements, functions. 父组件需要在constructor构造函数中继承,绑定的方法需要在构造函数中声明绑定。
constructor(props){ super(props); this.eventHandle = this.eventHandle.bind(this); }
在项目开中,有些约定验证通过下面的属性和方法完成,很方便。非ES6写法:
//组件类的PropTypes属性,就是用来验证组件实例的属性是否符合要求 var MyTitle = React.createClass({ propTypes:{ title : React.PropTypes.string.isRequired }, //getDefaultProps 方法可以用来设置组件属性的默认值。 getDefaultProps:function(){ return { title:'Hello' }; },
this.props 对象的属性与组件的属性一一对应,还有个特别就是 this.props.children 属性。它表示组件的所有子节点.
this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object.
class NodeList extends React.Component { constructor(props){ super(props); this.handleClick = this.handleClick.bind(this); } handleClick(e){ console.log(e.target.html()); } render(){ return ( <ul onClick={this.props.onNodeClik}> {React.Children.map(this.props.children,function(child){
return <li>{child}</li>;
})}
</ul>
);
}
}
ReactDOM.render(
<NodeList onNodeClick={this.handleClick}>
<span>Torri</span>
<span>Summer</span>
<span>Super</span>
</NodeList>,
document.getElementById(‘root’)
)
5.state 应用
state 是React 有一大创新,就是将组建看成一个状态机,在与用户交互的过程中,触发状态变化,从而重新渲染。
this.state和this.props不同,this.props定义那些不可变的属性,而this.state定义可变的状态属性。
在UI 开发过程中,分解组件后,要仔细考虑state应该控制在哪一层,一般在根组件。
class LinkButton extends React.Component { constructor(props){ super(props); this.handleClick = this.handleClick.bind(this); this.state = {like: true} } handleClick(){ this.setState({like: !this.state.like}); //避免直接更改state. this.setState()修改状态,修改完状态,会自动调用this.render方法,再次渲染组件。 }
render(){
var text = this.state.like;
return (
<p onClick={this.handleClick}>
Your said is {text}
</p>
);
}
}
ReactDOM.render(
<LinkButton />,
document.getElementById(’root’)
)
6.refs 认识
this.refs.[refName] 会返回这个真实的 DOM 节点。所以必须等到DOM插入页面之后访问。一般加在触发事件上,click,keydown,copy,scroll等等
下面三种情况就可以考虑使用refs:
- Managing focus, text selection, or media playback.
- Triggering imperative animations.
- Integrating with third-party DOM libraries.
var MyComponent = React.createClass({ handleClick:function(){ this.refs.myTextInput.focus(); }, cancelHandel:function(){ this.refs.myTextInput.value=""; }, render:function(){ return ( <div> <input type="text" ref="myTextInput"/> <input type="button" value="OK" onClick={this.handleClick}/> <input type="button" value="Cancel" onClick={this.cancelHandel}/> </div> ); } }) ReactDOM.render( <MyComponent/>, document.getElementById('container') )
7.context 应用
组件属性传递,除了通过props手动每一层组件传递之外,有一个更直接更强的API , named “context”
context 如何使用?下面是官网例子:
const PropTypes = require('prop-types'); class Button extends React.Component { render() { return ( <button style={{background: this.context.color}}> {this.props.children} </button> ); } } Button.contextTypes = { color: PropTypes.string }; class Message extends React.Component { render() { return ( <div> {this.props.text} <Button>Delete</Button> </div> ); } } class MessageList extends React.Component { getChildContext() { return {color: "purple"}; } render() { const children = this.props.messages.map((message) => <Message text={message.text} /> ); return <div>{children}</div>; } } MessageList.childContextTypes = { color: PropTypes.string };
在父组件 定义childContextTypes (包含属性及属性类型), getChildContext() 定义属性的初始值
在子组件 定义contextTypes 与父类定义的childContextTypes保持一致,这样在子组件用this.context.属性名 就可以拿到父组件对应的属性值。不再需要props来层层传递。
如果contextTypes 被定义在组件中,下面对应的方法也会多一个参数:
- constructor(props, context)
- componentWillReceiveProps(nextProps, nextContext)
- shouldComponentUpdate(nextProps, nextState, nextContext)
- componentWillUpdate(nextProps, nextState, nextContext)
- componentDidUpdate(prevProps, prevState, prevContext)
官网说不建议使用context,出于稳定性和开发者对React熟悉性的考虑。但是这个在开发Form组件上,应用起来很方便。Form 与个种子组件之间的属性传递会变得很精简。