00、react.js之 用法心得

来源:互联网 发布:linux snmptrap接收 编辑:程序博客网 时间:2024/06/16 02:44

1、setState高级用法

详细说明连接:http://huziketang.com/books/react/lesson10

setState 接受对象参数

setState 接受函数参数

这里还有要注意的是,当你调用 setState 的时候,React.js 并不会马上修改 state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state 当中,然后再触发组件更新。这一点要好好注意。可以体会一下下面的代码:

...  handleClickOnLikeButton () {    console.log(this.state.isLiked)    this.setState({      isLiked: !this.state.isLiked    })    console.log(this.state.isLiked)  }...

你会发现两次打印的都是 false,即使我们中间已经 setState 过一次了。这并不是什么 bug,只是 React.js 的 setState 把你的传进来的状态缓存起来,稍后才会帮你更新到 state 上,所以你获取到的还是原来的 isLiked

所以如果你想在 setState 之后使用新的 state 来做后续运算就做不到了,例如:

...  handleClickOnLikeButton () {    this.setState({ count: 0 }) // => this.state.count 还是 undefined    this.setState({ count: this.state.count + 1}) // => undefined + 1 = NaN    this.setState({ count: this.state.count + 2}) // => NaN + 2 = NaN  }...

上面的代码的运行结果并不能达到我们的预期,我们希望 count 运行结果是 3 ,可是最后得到的是 NaN。但是这种后续操作依赖前一个 setState 的结果的情况并不罕见。

这里就自然地引出了 setState 的第二种使用方式,可以接受一个函数作为参数。React.js 会把上一个 setState 的结果传入这个函数,你就可以使用该结果进行运算、操作,然后返回一个对象作为更新 state 的对象:

...  handleClickOnLikeButton () {    this.setState((prevState) => {      return { count: 0 }    })    this.setState((prevState) => {      return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1    })    this.setState((prevState) => {      return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3    })    // 最后的结果是 this.state.count 为 3  }...

这样就可以达到上述的利用上一次 setState 结果进行运算的效果。

2、state与props说明

state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState 方法进行更新,setState 会导致组件的重新渲染。
props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。
state 和 props 有着千丝万缕的关系。它们都可以决定组件的行为和显示形态。一个组件的 state 中的数据可以通过 props 传给子组件,一个组件可以使用外部传入的 props 来初始化自己的 state。但是它们的职责其实非常明晰分明:state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。
如果你觉得还是搞不清 state 和 props 的使用场景,那么请记住一个简单的规则:尽量少地用 state,尽量多地用 props。
没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。

3、组件的两种常用写法

React.js 非常鼓励无状态组件,在 0.14 版本引入了函数式组件——一种定义不能使用state 组件,例如一个原来这样写的组件:

class HelloWorld extends Component {  constructor() {    super()  }  sayHi () {    alert('Hello World')  }  render () {    return (      <div onClick={this.sayHi.bind(this)}>Hello World</div>    )  }}

用函数式组件的编写方式就是:

const HelloWorld = (props) => {  const sayHi = (event) => alert('Hello World')  return (    <div onClick={sayHi}>Hello World</div>  )}

以前一个组件是通过继承 Component 来构建,一个子类就是一个组件。而用函数式的组件编写方式是一个函数就是一个组件,你可以和以前一样通过 <HellWorld /> 使用该组件。不同的是,函数式组件只能接受 props 而无法像跟类组件一样可以在constructor 里面初始化 state。你可以理解函数式组件就是一种只能接受 props和提供 render 方法的类组件。

4、渲染列表数据

对于用表达式套数组罗列到页面上的元素,都要为每个元素加上 key 属性,这个 key 必须是每个元素唯一的标识。一般来说,key 的值可以直接后台数据返回的 id,因为后台的 id 都是唯一的。

详细说明链接:http://huziketang.com/books/react/lesson13

5、页面数据初始化说明

我们一般会把组件的 state 的初始化工作放在 constructor 里面去做;在 componentWillMount 进行组件的启动工作,例如 Ajax 数据拉取、定时器的启动;组件从页面上销毁的时候,有时候需要一些数据的清理,例如定时器的清理,就会放在 componentWillUnmount 里面去做。

6、props.children 和容器类组件

详细链接:http://huziketang.com/books/react/lesson22

使用自定义组件的时候,可以在其中嵌套 JSX 结构。嵌套的结构在组件内部都可以通过 props.children 获取到,这种组件编写方式在编写容器类型的组件当中非常有用。而在实际的 React.js 项目当中,我们几乎每天都需要用这种方式来编写组件。

7、组件的命名和方法的摆放顺序

插入一些小贴示,大家可以注意到我们组件的命名和方法的摆放顺序其实有一定的讲究,这里可以简单分享一下个人的习惯,仅供参考。
组件的私有方法都用 _ 开头,所有事件监听的方法都用 handle 开头。把事件监听方法传给组件的时候,属性名用 on 开头。例如:

<CommentInput onSubmit={this.handleSubmitComment.bind(this)} />

这样统一规范处理事件命名会给我们带来语义化组件的好处,监听(on)CommentInput 的 Submit 事件,并且交给 this 去处理(handle)。这种规范在多人协作的时候也会非常方便。
另外,组件的内容编写顺序如下:

1. static 开头的类属性,如 defaultProps、propTypes。
2. 构造函数,constructor。
3. getter/setter(还不了解的同学可以暂时忽略)。
4. 组件生命周期。
5. _ 开头的私有方法。
6. 事件监听方法,handle*。
7. render*开头的方法,有时候 render() 方法里面的内容会分开到不同函数里面进行,这些函数都以 render* 开头。
8. render() 方法。
如果所有的组件都按这种顺序来编写,那么维护起来就会方便很多,多人协作的时候别人理解代码也会一目了然。

8、使用循环时的性能优化

在 jsx 中使用循环,一般会用到`Array.prototype.map`(来自ES5标准)

```jsx
class Helloextends React.Component {
render() {
const arr = ['a','b', 'c']
return (
<div>
{arr.map((item,index) => {
return <pkey={index}>this is {item}</p>
})}
</div>
)
}
}
```

注意,`arr.map`是包裹在`{}`中的,`key={index}`有助于React的渲染优化,jsx中的`{}`可放一个可执行的 js 程序或者变量

9、React性能优化


# React 性能优化

## 简单的 todo-list-demo

讲 React 性能优化不能光靠嘴说,得有一个 demo 作为依托,做一个简单的 todolist demo,根据源代码来讲解。顺便体验一下 React 最简单的用法。

## 性能检测

安装 react 性能检测工具 `npm i react-addons-perf --save`,然后在`./app/index.jsx`

```js
// 性能测试
import Perffrom 'react-addons-perf'
if (__DEV__) {
window.Perf =Perf
}
```
运行程序。在操作之前先运行`Perf.start()`开始检测,然后进行若干操作,运行`Perf.stop`停止检测,然后再运行`Perf.printWasted()`即可打印出浪费性能的组件列表。在项目开发过程中,要经常使用检测工具来看看性能是否正常。

如果性能的影响不是很大,例如每次操作多浪费几毫秒、十几毫秒,个人以为没必要深究,但是如果浪费过多影响了用户体验,就必须去搞定它。

## PureRenderMixin 优化

React 最基本的优化方式是使用[PureRenderMixin](http://reactjs.cn/react/docs/pure-render-mixin.html),安装工具`npm i react-addons-pure-render-mixin --save`,然后在组件中引用并使用

```jsx
import Reactfrom 'react'
import PureRenderMixinfrom 'react-addons-pure-render-mixin'

class Listextends React.Component {
constructor(props,context) {
super(props,context);
this.shouldComponentUpdate =PureRenderMixin.shouldComponentUpdate.bind(this);
}
//...省略其他内容...
}
```

React 有一个生命周期 hook 叫做`shouldComponentUpdate`,组件每次更新之前,都要过一遍这个函数,如果这个函数返回`true`则更新,如果返回`false`则不更新。而默认情况下,这个函数会一直返回`true`,就是说,如果有一些无效的改动触发了这个函数,也会导致无效的更新

那么什么是无效的改动?之前说过,组件中的`props``state`一旦变化会导致组件重新更新并渲染,但是如果`props``state`没有变化也莫名其妙的触发更新了呢(这种情况确实存在)———— 这不就导致了无效渲染吗?

这里使用`this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);`的意思是重写组件的`shouldComponentUpdate`函数,在每次更新之前判断`props``state`,如果有变化则返回`true`,无变化则返回`false`

因此,我们在开发过程中,在每个 React 组件中都尽量使用`PureRenderMixin`


## Immutable.js 优化

React 的终极优化是使用 [Immutable.js](https://facebook.github.io/immutable-js/) 来处理数据,Immutable 实现了 js 中不可变数据的概念(不了的同学可以去查一下何为“不可变数据”)。

但是也不是所有的场景都适合用它,当我们组件的`props``state`中的数据结构层次不深(例如普通的数组、对象等)的时候,就没必要用它。但是当数据结构层次很深(例如`obj.x.y.a.b = 10`这种),你就得考虑使用了。

之所以不轻易使用是,Immutable 定义了一种新的操作数据的语法,如下。和我们平时操作 js 数据完全不一样,而且每个地方都得这么用,学习成本高、易遗漏,风险很高。

```js
var map1 =Immutable.Map({a:1,b:2, c:3});
var map2 =map1.set('b',50);
map1.get('b');// 2
map2.get('b');// 50
```

因此,这里建议优化还是要从设计着手,尽量把数据结构设计的扁平一些,这样既有助于优化系统性能,又减少了开发复杂度和开发成本。

本教程中不会使用 Immutable.js