一、初入React世界
1.5 React声明周期
react生命周期分为两类:
当组件在挂载或卸载时
当组件接收新的数据时,即组件更新时
初始化组件
import React, { Component, PropTypes } from 'react';
class App extends Component {
// 类型检查
static propTypes = {
// ...
};
// 默认类型
static defaultProps = {
// ...
};
constructor(props) {
super(props);
this.state = {
// ...
};
}
componentWillMount() {
// ...
}
// 在其中使用setState 会更新组件
componentDidMount() {
// ...
}
render() {
return <div>This is a demo.</div>
}
}
挂载或卸载过程
组件的挂载
- componentWillMount( )
- render( )
- componentDidMount( )
组件的卸载
- componentWillUnmount( ),执行一些清理方法,如事件回收或是清除定时器
数据更新过程
更新过程指父组件向下传递props或组件自身执行setState方法时发生的一系列更新动作。
- componentWillReceiveProps(nextProps)
componentWillReceiveProps(nextProps){
// this.setState({});
}
- shouldComponentUpdate(nextProps,nextState)
shouldComponentUpdate(nextProps,nextState){
// 条件判断
// return true;
}
- componentWillUpdate(nextProps,nextState) 此时不能执行setState
- render( )
- componentDidUpdate(prevProps,prevState)
如果自身的state更新了,那么会依次执行shouldComponentUpdate、componentWillUpdate 、render 和 componentDidUpdate。
如果组件是由父组件更新 props 而更新的,那么在 shouldComponentUpdate 之前会先执行componentWillReceiveProps 方法。
1.6 React与DOM
1.6.1 ReactDOM
其API非常少,只有findDOMNode,unmountComponentAtNode和render
- findDOMNode
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class App extends Component {
componentDidMount() {
// this 为当前组件的实例
const dom = ReactDOM.findDOMNode(this);
}
render() {}
}
findDOMNode只对已经挂载的组件有效,DOM 真正被添加到 HTML 中的生命周期方法是componentDidMount 和 componentDidUpdate 方法
- render
把 React 渲染的Virtual DOM 渲染到浏览器的 DOM 当中,就要使用 render 方法
1.6.2 ReactDOM的不稳定方法
unstable_renderSubtreeIntoContainer( ),它与render方法的区别在于是否传入父节点
1.6.3 refs
指向组件的实例
二、漫谈React
2.1 事件系统
React基于 Virtual DOM实现了一个SyntheticEvent(合成事件)层,我们呢所定义的事件会接收到一个SyntheticEvent对象的实例。所有事件都自动绑定到最外层上。
<button onClick={this.handleClick}>点击</button>
事件委托
事件处理函数绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象;当时间发生时,首先被这个统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用。
自动绑定
在React组件中,自动绑定this为当前组件。但在使用ES6 classes或纯函数时,这种自动绑定就不存在了,需要手动实现绑定。
- bind方法
- 构造器内声明 完成绑定
- 箭头函数
在componentDidMount中完成原生事件的绑定,一定要在组件卸载时手动移除。
2.1.5 对比React合成事件与JS原生事件
事件传播与阻止事件传播
原生:捕获阶段、事件处理阶段、冒泡阶段。阻止传播兼容问题。(IE和DOM2)
react:没有捕获阶段。阻止传播e.preventDefault( )
事件类型
合成事件的实践类型是原生事件类型的一个子集
事件绑定方式
事件对象
原生有兼容问题(IE和W3C标准),react不存在兼容问题
2.2 表单
受控组件
每当表单的状态发生变化时,都会被写入到组件的state中,这种组件在React中被称为受控组件(controlled component)。
受控组件更新state的流程:
(1)可以通过在初始 state 中设置表单的默认值
(2)每当表单的值发生变化时,调用onChange事件处理器
(3)事件处理器通过合成事件对象e拿到改变后的状态,并更新state
(4)setState触发视图的重新渲染,完成表单组件值得更新
2.3 样式处理
基本样式设置
- className prop添加自定义样式
- 行内样式要使用对象设置
const style={
color:'white',
// 渲染成10px
height:10
};
const component = <Component style={style}/>
CSS Modules
此处见P79页
2.4 组件间通信
2.4.1 父组件向子组件通信
通过props 向子组件传递信息
2.4.2 子组件向父组件通信
- 利用回调函数
- 利用自定义事件机制
2.4.3 跨级组件通信
React中,我们可以使用 context 来实现跨级父子组件间的通信
// listItem组件
class ListItem extends Component {
static contextTypes = {
color: PropTypes.string,
};
render() {
const { value } = this.props;
return (
<li style={{background: this.context.color}}>{value}</li>
)
}
}
// List组件
class List extends Component {
static childContextTypes = {
color: PropTypes.string,
};
getChildContext() {
return {
color: 'red'
}
}
}
父组件中定义了 ChildContext,这样从这一层开始的子组件都可以拿到定义的context。
2.5 组件间抽象
高阶组件(HOC)
它接受React组件作为输入,输出一个新的React组件。
实现高阶组件的方法:
1.属性代理
高阶组件通过被包裹的React组件来操作props,使原始组件具备高阶组件对它的修饰。
属性代理构建高阶组件时,执行生命周期的过程类似于堆栈调用:
didmount--HOC didmount-- (HOCs didmount)--(HOCs willUnMount)--HOC willUnMount--unmount
import React, { Component } from 'react';
const MyContainer = (WrappedComponent) => {
class extends Component {
render() {
return <WrappedComponent {...this.props}>
}
}
}
当然我们也可以用 decorator 语法糖来转换,简化了高阶组件的调用
import React, { Component } from 'react';
@MyContainer
class MyComponent extends Component {
render();
}
export default MyComponent;
- 控制props
// 新组件会多一个text的prop
import React, { Component } from 'react';
const MyContainer = (WrappedComponent) => {
class extends Component {
render() {
const newProps = {
text: newText,
};
return <WrappedComponent {...this.props} {...newProps}>
}
}
}
- 通过refs使用引用
高阶组价中,我们可以接受 refs 使用 WrappedComponent 的引用。
import React, { Component } from 'react';
const MyContainer = (WrappedComponent) => {
class extends Component {
proc(wrappedComponentInstance) {
wrappedComponentInstance.method();
}
render() {
const props = Object.assign({}, this.props, {
ref: this.proc.bind(this),
});
return <WrappedComponent {...props}>
}
}
}
- 抽象state
抽象一个input组件
import React, { Componet } from 'react';
const MyContainer = (WrappedComponent) => {
class extends Component {
constructor(props) {
super(props);
this.state = {
name:'',
}
this.onNameChange = this.onNameChange.bind(this);
}
}
onNameChange(event) {
this.setState({
name: event.target.value,
});
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onNameChange,
}
}
return <WrappedComponent {...this.props} {...newProps} />
}
}
通过这样的封装,我们就得到了一个被控制的input组件
@MyContainer
class MyComponent extends Component {
render() {
return <input name="name" {...this.props.name}>
}
}
2.反向继承
高阶组件继承于被包裹的React组件,HOC的调用顺序和队列是一样的。
didmount--HOC didmount-- (HOCs didmount)--willUnMount--HOC willUnMount--(HOCs willUnMount)
- 渲染劫持
指高阶组件可以控制WrappedComponent的渲染过程。不能操作组件的子组件。
// 实例一:条件渲染
const MyContainer = (WrappedComponent) => {
class extends WrappedComponent {
render() {
if(this.props.loggedIn) {
return super.render();
} else {
return null;
}
}
}
}
// 实例二:对render结果进行修改
const MyContainer = (WrappedComponent) => {
class extends WrappedComponent {
render() {
const elementsTree = super.render();
let newProps = {};
if(elementsTree && elementsTree.type === 'input'){
newProps = {value: 'May the force be with you'};
}
const props = Object.assign({}, elementsTree.props, newProps);
const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.childre);
return newElementsTree;
}
}
}
- 控制state
4. 组件参数
调用高阶组件时传入一些参数
import React, { Component } from 'react';
function HOCFactory(...params) {
return function HOCFactory(WrappedComponent) {
return class HOC extends Component {
render() {
return <WrappedComponent {...this.props}>
}
}
}
}
// 使用
HOCFactoryFactory(params)(WrappedComponent);
// 或者
@HOCFactoryFactory(params);
class WrappedComponent extends React.Component{}
2.6 组件的性能优化
纯函数
- 给定相同的输入,它总能返回相同的输出
- 过程没有副作用
- 没有额外的状态依赖
PureRender
原理:重新实现了 shouldComponentUpdate 生命周期方法,让当前传入的 props
和 state 与之前的作浅比较
// 使用
import React, { Component } from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
class App extends Component{
constructor(props){
super(props);
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
render(){
return <div className={this.props.className}>foo</div>
}
}
- 设置子组件
对于设置了子组件的React组件,在调用shouldComponentUpdate时,均返回true。这时,为免组件重复渲染,给父组件设置PureRender
Immutable
Immutable Data一旦创建,就不能再更改。对Immutable对象进行修改添加删除操作都会返回一个新的Immutable对象。
运用Immutable.js 修改shouldComponentUpdate,可以极大的提高性能。
key
标识当前项的唯一性,用来做Virtual DOM diff
react-addons-perf
官方插件,性能测试工具