1、 Performance面板
1. FPS、CPU和NET
![](https://img.haomeiwen.com/i7343547/e62c81f6570804dd.png)
FPS(frames per second):是用来分析动画的一个主要性能指标,理论上是越高越好,(但过高的刷新率并没有实际意义,一般人很难分辨出60帧/秒与100帧/秒有什么不同。)能达到60帧/秒的话,在用户体验度的方向上是不错的存在,在FPS图表中如果存在红色的长条,说明帧存在严重的问题,可能导致非常差的用户体验,一般来说,绿色的长条越高,说明FPS越高,用户体验越好。
CPU:鼠标点击CPU图标,下方的Summary面板上会呈现此时刻CPU使用信息,各种颜色代表着在这个时间段内,CPU在各种处理上所花费的时间。
NET:每条彩色横杠表示一种资源。横杠越长,检索资源所需的时间越长。 每个横杠的浅色部分表示等待时间(从请求资源到第一个字节下载完成的时间)
PS 显示实时FPS面板,实时展示页面的FPS指标
1、按下 Command+Shift+P(Mac)或者 Control+Shift+P(Windows, Linux) 打开命令菜单
2、输入Rendering,点选Show Rendering
3、在Rendering面板里,激活FPS Meter。FPS实时面板就出现在页面的右上方。
4、按下Esc关闭FPS Meter
-
Summary面板
![](https://img.haomeiwen.com/i7343547/9e3a1991883a89b3.png)
summary面板为总结面板,从宏观层面上概括了浏览器加载的总时间,包括以下几个部分
颜色 | 英文 | 中文 |
---|---|---|
蓝色 | Loading | 加载 |
黄色 | Scripting | 脚本 |
紫色 | Rendering | 渲染 |
绿色 | Painting | 绘制 |
灰色 | Other | 其他 |
白色 | Idle | 空闲 |
-
Bottom-Up和Call Tree
Bottom-Up和Call Tree两者都是直观地分析浏览器对页面的build精确到毫秒级的情况,前者是The Heavy (Bottom Up) view is available in the Bottom-Up tab,类似于事件冒泡;后者是And the Tree (Top Down) view is available in the Call Tree tab,类似于事件捕获。
-
Event Log
![](https://img.haomeiwen.com/i7343547/17322b6db365754c.png)
Event Log可以查看某一阶段的日志,点击各个模块的按钮,查看日志信息
2、消除常见react性能问题
-
将经常更新的区域提取为孤立的组件
在react应用程序中最常遇见兼得问题就是渲染问题,当渲染一个大型Dom树时会消耗很多时间,大部分时候,并不需要将整个DOm重新渲染,而是只改变某个分支上的某一个值,频繁的渲染操作将会造成渲染上的浪费,一个比较好的方式是将经常更新的区域拆分成一个单独的组件。
父组件
import React from 'react';
import Title from './Title/index';
export default class SuperList extends React.Component { // 首次渲染会渲染两次
constructor(props) {
super(props)
this.state = {
items: [1, 2, 3],
}
}
addItem = () => {
const self = this;
let {items} = self.state;
items.push(4);
this.setState({ items });
}
render() {
console.log('父组件 render')
const { items, obj } = this.state;
return (
<div>
<ul>
{items.map((e, i) => {
return <li key={i}>数组第{i}个值为:{e}</li>
})}
</ul>
<button onClick={this.addItem}>新增数组值</button>
<Title />
</div>
)
}
}
子组件
import React from 'react';
export default class Title extends React.Component {
constructor(props) {
super(props);
this.state = {
title: 'title',
}
// this.changeTitle = this.changeTitle.bind(this); // 当使用ES5编写函数changeTitle() {}时,必须使用这个
}
changeTitle = () => {
const self = this;
let {title} = self.state;
title = '你好';
this.setState({ title });
}
render() {
console.log('子组件render')
return (
<div>
<div>子组件title值:{this.state.title}</div>
<button onClick={this.changeTitle}>更改title值</button>
</div>
)
}
}
在上述组件中将DOM树拆分存储为两个组件,将频繁渲染DOM的部分单独拆分成一个组件,当富组件渲染时,会带动子组件进行渲染,但子组件渲染时,在此组件条件下,父组件中的DOM不会被重新渲染,需要渲染的DOM树减少,能够增加渲染的速度。
-
使用纯组件方式
使用纯组件的前提是组件的props与之前的props不同,这样的话当前组件才会被重新渲染,实现纯组件的简单方法是使用React.PureComponent,而不是使用默认组件React.Component,React.PureComponent与React.Component的区别在于React.Component没有实现shouldComponentUpdate(),而React.PureComponent通过浅的props和state比较来实现它。
⚠️ React.PureComponent中的shouldComponentUpdate()只是浅解析对象,对于包含复杂的数据结构,可能会出现渲染错误
当使用React.PureComponent时,希望props中具有简单的数据变化,或者当编码人员知道有深层的数据变化时使用forceUpdate()对数据做更新渲染DOM树。
父组件
import React from 'react';
import Title from './Title/index';
export default class SuperList extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
items: [1, 2, 3],
obj: {a: 1, b: 2},
title: '早上好',
}
}
addItem = () => {
const self = this;
let {items, obj } = self.state;
items.push(4);
obj.b = 3;
// this.forceUpdate();
this.setState({ items, obj });
}
render() {
console.log('父组件 render')
const { title, items, obj } = this.state;
return (
<div>
<ul>
{items.map((e, i) => {
return <li key={i}>数组第{i}个值为:{e}</li>
})}
</ul>
<div>对象中的值b为:{obj.b}</div>
<button onClick={this.addItem}>新增数组值</button>
<Title title={title} />
</div>
)
}
}
子组件
import React from 'react';
export default class Title extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
title: 'title',
}
}
// shouldComponentUpdate(nextProps) {
// if (nextProps.title !== this.props.title) {
// return true;
// }
// return false;
// }
changeTitle = () => {
const self = this;
let {title} = self.state;
title = '你好';
this.setState({ title });
}
render() {
console.log('子组件render')
return (
<div>
<div>子组件title值:{this.state.title}</div>
<button onClick={this.changeTitle}>更改title值</button>
</div>
)
}
}
使用React.PureComponent可以解决React.Component在首次渲染时会渲染两次的问题,因为React.PureComponent是浅比较是否有质的改变,当值不存在改变时不会渲染。上面也会出现一个问题,当父组件点击改变数组按钮时,改组件也不会被渲染,因为发生改变的是数组和对象,即使是重新的setState了,但数组和对象的地址并未发生变化,所以不会有渲染发生,随后使用的forceUpdata()方法强制刷新,会使组件重新渲染。
除了使用forceUpdate()函数之外,还可以使用以下几种方式(但都没有forceUpdate()实行简单):
- 深拷贝
add() {
let items =JSON.parse(JSON.stringify(this.state.items));//黑科技
items.push(4);
this.setState({ items })
}
- 数组使用concat,对象使用Object.assign()
add() {
let { items } = this.state;
items=items.concat(4) //此时的items是一个新数组
this.setState({ items })
}
当使用了React.PureComponent后不需要使用shouldComponentUpdate()方法,当两者全部使用时会出现如下提示,并且在上述代码中会根据shouldComponentUpdate()来判断是否渲染当前的DOM树
Warning: SuperList has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.
-
避免将新对象作为传递参数
只要props发生变化,就会重新渲染,此时存在一种情况就是,当传入一个对象时,两个对象比较,值未发生改变,但却并不指向同一地址,此时react会认为当前是有改变的,因此会重新渲染。
父组件
import React from 'react';
import Title from './Title/index';
export default class SuperList extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
title: '你好',
name: '我是XXX',
preObj: { title: '你好' }
}
}
addItem = () => {
const self = this;
let { name, preObj } = self.state;
// let { name, preObj, title } = self.state;
name = name + name;
// title = title + title
preObj.title = preObj.title + preObj.title;
// this.setState({ name, title, preObj });
this.setState({ name, preObj });
}
render() {
console.log('父组件 render')
const { title, name, preObj } = this.state;
const obj = {
title
};
return (
<div>
<div>对象中的值b为:{name}</div>
<button onClick={this.addItem}>改变姓名</button>
{/* <Title title={title} /> */}
{/* <Title title={obj.title} /> */}
{/* <Title obj={preObj} /> */}
<Title obj={obj} />
</div>
)
}
}
子组件
import React from 'react';
export default class Title extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
title: 'title',
}
}
render() {
console.log('子组件render')
return (
<div>
<div>子组件title值:{this.props.obj.title}</div>
{/* <div>子组件title值:{this.props.title}</div> */}
</div>
)
}
}
在每次渲染时调用了新的对象,因此props传递给<Title>的值就被视为新值,因此会导致组件重新渲染
-
父组件传递函数时优化
绑定this的方式,一般有以下几种:
1、constructor中绑定(es5)
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this); //构造函数中绑定
}
//然后可以
<p onClick={this.handleClick}>
在es6中可以直接写成以下方式不需要绑定
<p onClick={this.handleClick}>
2、使用时绑定(es5)
<p onClick={this.handleClick.bind(this)}>
3、箭头函数
<p onClick={() => { this.handleClick() }}>
三种方式哪一种的性能更好呢个?
-
第一种,构造函数每一次渲染的时候只会执行一遍;(最nice)
-
第二种方法,在每次render()的时候都会重新执行一遍函数;
-
三种方法的话,每一次render()的时候,都会生成一个新的箭头函数
网友评论