前言
React 是 Facebook 开发的构建用户界面的类库. 它从设计之初就将性能作为重点,在应用时更是能够采取一些策略而后咱们网站性能更加优化,以下是我平时用到的一些优化形式,心愿能够帮忙到大家!
Code Splitting
Code Splitting 能够帮你“懒加载”代码,如果你没方法间接缩小利用的体积,那么无妨尝试把利用从单个 bundle 拆分成单个 bundle + 多份动静代码的模式。 webpack提供三种代码拆散办法,详情见webpack官网
1.入口终点:应用 entry 配置手动地拆散代码。
2.避免反复:应用 SplitChunks 去重和拆散 chunk。
3.动静导入:通过模块的内联函数调用来拆散代码。
在此,次要理解一下第三种动静导入的办法。
1、例如能够把上面的import形式
import { add } from './math';
console.log(add(16, 26));
复制代码
改写成动静 import 的模式,让首次加载时不去加载 math 模块,从而缩小首次加载资源的体积。
import("./math").then(math => {
console.log(math.add(16, 26));
});
复制代码
2、例如援用react的高阶组件react-loadable进行动静import。
import Loadable from 'react-loadable';
import Loading from './loading-component';
const LoadableComponent = Loadable({
loader: () => import('./my-component'),
loading: Loading,
});
export default class App extends React.Component {
render() {
return <LoadableComponent/>;
}
}
复制代码
下面的代码在首次加载时,会先展现一个 loading-component,而后动静加载 my-component 的代码,组件代码加载结束之后,便会替换掉 loading-component
shouldComponentUpdate防止反复渲染
当一个组件的props或者state扭转时,React通过比拟新返回的元素和之前渲染的元素来决定是否有必要更新理论的DOM。当他们不相等时,React会更新DOM。
在一些状况下,你的组件能够通过重写这个生命周期函数shouldComponentUpdate来晋升速度, 它是在从新渲染过程开始前触发的。 这个函数默认返回true,可使React执行更新。
为了进一步阐明问题,援用官网的图解释一下,如下图( SCU示意shouldComponentUpdate,绿色示意返回true(须要更新),红色示意返回false(不须要更新);vDOMEq示意虚构DOM比对,绿色示意统一(不须要更新),红色示意产生扭转(须要更新))
依据渲染流程,首先会判断shouldComponentUpdate(SCU)是否须要更新。如果须要更新,则调用组件的render生成新的虚构DOM,而后再与旧的虚构DOM比照(vDOMEq),如果比照统一就不更新,如果比照不同,则依据最小粒度扭转去更新DOM;如果SCU不须要更新,则间接放弃不变,同时其子元素也放弃不变。
C1根节点,绿色SCU、红色vDOMEq,示意须要更新。
C2节点,红色SCU,示意不须要更新,同时C4、C5作为其子节点也不须要查看更新。
C3节点,绿色SCU、红色vDOMEq,示意须要更新。
C6节点,绿色SCU、红色vDOMEq,示意须要更新。
C7节点,红色SCU,示意不须要更新。
C8节点,绿色SCU,示意React须要渲染这个组件;
绿色vDOMEq,示意虚构DOM统一,不更新DOM。
因而,咱们能够通过依据本人的业务个性,重载shouldComponentUpdate,只在确认实在DOM须要扭转时,再返回true。个别的做法是比拟组件的props和state是否真的发生变化,如果发生变化则返回true,否则返回false。援用官网的案例。
class CounterButton extends React.Component {
constructor(props) {
super(props);this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) { return true;}if (this.state.count !== nextState.count) { return true;}return false;
}
render() {
return ( <button color={this.props.color} onClick={() => this.setState(state => ({count: state.count + 1}))}> Count: {this.state.count} </button>);
}
}
复制代码
在以上代码中,shouldComponentUpdate只查看props.color和state.count的变动。如果这些值没有变动,组件就不会更新。当你的组件变得更加简单时,你能够应用相似的模式来做一个“浅比拟”,用来比拟属性和值以断定是否须要更新组件。这种模式非常常见,因而React提供了一个辅助对象来实现这个逻辑 - 继承自React.PureComponent。
大部分状况下,你能够应用React.PureComponent而不用写你本人的shouldComponentUpdate,它只做一个浅比拟。然而当你比拟的指标为援用类型数据,浅比拟会疏忽属性或状态渐变的状况,此时你不能应用它,此时你须要关注上面的不可渐变数据。
附:数据渐变(mutated)是指变量的援用没有扭转(指针地址未扭转),然而援用指向的数据产生了变动(指针指向的数据产生变更)。例如const x = {foo:'foo'}。x.foo='none' 就是一个渐变。
应用不可渐变数据结构
援用官网中的例子解释一下渐变数据产生的问题。例如,假如你想要一个ListOfWords组件来渲染一个逗号分隔的单词列表,并应用一个带了点击按钮名字叫WordAdder的父组件来给子列表增加一个单词。以下代码并不正确:
class ListOfWords extends React.PureComponent {
render() {
return <div>{this.props.words.join(',')}</div>;
}
}
class WordAdder extends React.Component {
constructor(props) {
super(props);this.state = { words: ['marklar']};this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 这段内容将会导致代码不会依照你预期的后果运行const words = this.state.words;words.push('marklar');this.setState({words: words});
}
render() {
return ( <div> <button onClick={this.handleClick} /> <ListOfWords words={this.state.words} /> </div>);
}
}
复制代码
导致代码无奈失常工作的起因是 PureComponent 仅仅对 this.props.words的新旧值进行“浅比拟”。在words值在handleClick中被批改之后,即便有新的单词被增加到数组中,然而this.props.words的新旧值在进行比拟时是一样的(援用对象比拟),因而 ListOfWords 始终不会产生渲染。 防止此类问题最简略的形式是防止应用值可能会渐变的属性或状态,如:
1、数组应用concat,对象应用Object.assign()
handleClick() {
this.setState(prevState => ({
words: prevState.words.concat(['marklar'])
}));
}
复制代码
// 假如咱们有一个叫colormap的对象,上面办法不净化原始对象
function updateColorMap(colormap) {
return Object.assign({}, colormap, {right: 'blue'});
}
复制代码
2、ES6反对数组或对象的spread语法
handleClick() {
this.setState(prevState => ({
words: [...prevState.words, 'marklar'],
}));
};
复制代码
function updateColorMap(colormap) {
return {...colormap, right: 'blue'};
}
复制代码
3、应用不可渐变数据immutable.js
immutable.js使得变动跟踪很不便。每个变动都会导致产生一个新的对象,因而咱们只需查看索引对象是否扭转。
const SomeRecord = Immutable.Record({ foo: null });
const x = new SomeRecord({ foo: 'bar' });
const y = x.set('foo', 'baz');
x === y; // false
复制代码
在这个例子中,x渐变后返回了一个新的索引,因而咱们能够平安的确认x被扭转了。 不可渐变的数据结构帮忙咱们轻松的追踪对象变动,从而能够疾速的实现shouldComponentUpdate。
组件尽可能的进行拆分、解耦
组件尽可能的细分,比方一个input+list组件,能够将list分成一个PureComponent,只在list数据变动时更新。否则在input值变动页面从新渲染的时候,list也须要进行不必要的DOM diff。
列表类组件优化
key属性在组件类之外提供了另一种形式的组件标识。通过key标识,在组件产生增删改、排序等操作时,能够依据key值的地位间接调整DOM程序,通知React 防止不必要的渲染而防止性能的节约。
var items = sortBy(this.state.sortingAlgorithm, this.props.items);
return items.map(function(item){
return <img src={item.src} />
});
复制代码
当程序产生扭转时,React 会对元素进行diff操作,并改img的src属性。显示,这样的操作效率是非常低的。这时,咱们能够为组件增加一个key属性以惟一的标识组件:
return <img src={item.src} key={item.id} />
复制代码
减少key后,React就不是diff,而是间接应用insertBefore操作挪动组件地位,而这个操作是挪动DOM节点最高效的方法。
bind函数
绑定this的形式:个别有上面3种形式:
1、constructor绑定
constructor(props) {
super(props);this.handleClick = this.handleClick.bind(this); //构造函数中绑定
}
//而后能够
<p onClick={this.handleClick}>
复制代码
2、应用时绑定
<p onClick={this.handleClick.bind(this)}>
复制代码
3、应用箭头函数
<Test click={() => { this.handleClick() }}/>
复制代码
以上三种办法,
第一种最优。因为第一种构造函数只在组件初始化的时候执行一次。
第二种组件每次render都会执行。
第三种在每一次render时候都会生成新的箭头函数。例:Test组件的click属性是个箭头函数,组件从新渲染的时候Test组件就会因为这个新生成的箭头函数而进行更新,从而产生Test组件的不必要渲染。
不要滥用props
props尽量只传须要的数据,防止多余的更新,尽量避免应用{...props}
ReactDOMServer进行服务端渲染组件
为了用户会更疾速地看到残缺渲染的页面,能够采纳服务端渲染技术,在此理解一下ReactDOMServer。
为了实现SSR,你可能会用nodejs框架(Express、Hapi、Koa)来启动一个web服务器,接着调用 renderToString 办法去渲染你的根组件成为字符串,最初你再输入到 response。
// using Express
import { renderToString } from "react-dom/server";
import MyPage from "./MyPage";
app.get("/", (req, res) => {
res.write("<!DOCTYPE html><html><head><title>My Page</title></head><body>");
res.write("<div id='content'>");
res.write(renderToString(<MyPage/>));
res.write("</div></body></html>");
res.end();
});
复制代码
客户端应用render办法来生成HTML
import ReactDOM from 'react-dom';
import MyPage from "./MyPage";
ReactDOM.render(<MyPage />, document.getElementById('app'));
复制代码
react性能检测工具
react16版本之前,咱们能够应用react-addons-perf工具来查看,而在最新的16版本,咱们只须要在url后加上?react_pref。
首先来理解一下react-addons-perf。 react-addons-perf这是 React 官网推出的一个性能工具包,能够打印出组件渲染的工夫、次数、浪费时间等。 简略说几个api,具体用法可参考官网:
Perf.start() 开始记录
Perf.stop() 完结记录
Perf.printInclusive() 查看所有设计到的组件render
Perf.printWasted() 查看不须要的节约组件render
再来理解一下,react16版本的办法,在url后加上?react_pref,就能够在chrome浏览器的performance,咱们能够查看User Timeing来查看组件的加载工夫。点击record开始记录,留神记录时长不要超过20s,否则可能导致chrome挂起。
最初
如果你感觉此文对你有一丁点帮忙,点个赞。或者能够退出我的开发交换群:1025263163互相学习,咱们会有业余的技术答疑解惑
如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点star: https://gitee.com/ZhongBangKe...不胜感激 !