[http://www.zhufengpeixun.cn/2020/html/62.4.react-optimize.html#t65.%20%E9%95%BF%E5%88%97%E8%A1%A8%E4%BC%98%E5%8C%96](http://www.zhufengpeixun.cn/2020/html/62.4.react-optimize.html#t65.%20%E9%95%BF%E5%88%97%E8%A1%A8%E4%BC%98%E5%8C%96)
****
骨架屏模板
[https://danilowoz.com/create-content-loader/](https://danilowoz.com/create-content-loader/)
## 1\. 使用React.Fragment
* 使用`React.Fragment`來避免向 DOM 添加額外的節點
~~~
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Users extends React.Component {
render() {
return (
<React.Fragment>
<div>用戶1</div>
<div>用戶2</div>
</React.Fragment>
);
}
}
ReactDOM.render(<Users />, document.querySelector('#root'));
~~~
## 2\. 使用 React.Lazy 延遲加載組件
* `React.Lazy`幫助我們按需加載組件,從而減少我們應用程序的加載時間,因為只加載我們所需的組件。
* `React.lazy`接受一個函數,這個函數內部調用 import() 動態導入。它必須返回一個 Promise,該 Promise 需要 resolve 一個 defalut export 的 React 組件。
* React.Suspense 用于包裝延遲組件以在加載組件時顯示后備內容。
~~~
import React, { Component,lazy, Suspense } from 'react'
import ReactDOM from 'react-dom';
import Loading from './components/Loading';
const AppTitle = lazy(()=>import(/* webpackChunkName: "title" */'./components/Title'))
class App extends Component{
state = {visible:false}
show = ()=>{
this.setState({visible:true});
}
render() {
return (
<>
{this.state.visible&&(
<Suspense fallback={<Loading/>}>
<AppTitle/>
</Suspense>
)}
<button onClick={this.show}>加載</button>
</>
)
}
}
ReactDOM.render(<App />, document.querySelector('#root'));
~~~
## 3\. 錯誤邊界(Error Boundaries)
* 如果當一個組件異步加載下載js文件時,網絡錯誤,無法下載 js 文件
* Suspense 無法處理這種錯誤情況, 在 react 中有一個 錯誤邊界 (Error Boundaries)的概念,用來解決這種問題,它是利用了 react 生命周期的 componetDidCatch 方法來處理
* 有兩種方式,一種是 生命周期 componentDidCatch 來處理錯誤,還有一種 是 靜態方法 static getDerivedStateFromError 來處理錯誤,
* 請使用`static getDerivedStateFromError()`渲染備用 UI ,使用 componentDidCatch() 打印錯誤信息。
~~~
import React, { Component,lazy, Suspense } from 'react'
import ReactDOM from 'react-dom';
import Loading from './components/Loading';
const AppTitle = lazy(()=>import(/* webpackChunkName: "title" */'./components/Title'))
class App extends Component{
state = {visible:false,isError: false}
show = ()=>{
this.setState({visible:true});
}
static getDerivedStateFromError(error) {
return { isError: true };
}
componentDidCatch (err, info) {
console.log(err, info)
}
render() {
if (this.state.isError) {
return (<div>error</div>)
}
return (
<>
{this.state.visible&&(
<Suspense fallback={<Loading/>}>
<AppTitle/>
</Suspense>
)}
<button onClick={this.show}>加載</button>
</>
)
}
}
ReactDOM.render(<App />, document.querySelector('#root'));
~~~
## 4\. PureComponent
* 當一個組件的`props`或`state`變更,React 會將最新返回的元素與之前渲染的元素進行對比,以此決定是否有必要更新真實的 DOM,當它們不相同時 React 會更新該 DOM。
* 如果渲染的組件非常多時可以通過覆蓋生命周期方法 shouldComponentUpdate 來進行優化
* shouldComponentUpdate 方法會在重新渲染前被觸發。其默認實現是返回 true,如果組件不需要更新,可以在`shouldComponentUpdate`中返回 false 來跳過整個渲染過程。其包括該組件的 render 調用以及之后的操作
* PureComponent通過prop和state的淺比較來實現`shouldComponentUpdate`
### 3.1 App.js
~~~
import React from 'react';
import {Button,message} from 'antd';
import PureComponent from './PureComponent';
export default class App extends PureComponent{
state = {
title:'計數器',
number:0
}
add = ()=>{
this.setState({number:this.state.number+parseInt(this.amount.value)});
}
render(){
console.log('App render');
return (
<div>
<Title2 title={this.state.title}/>
<Counter number={this.state.number}/>
<input ref={inst=>this.amount = inst}/>
<button onClick={this.add}>+</button>
</div>
)
}
}
class Counter extends PureComponent{
render(){
console.log('Counter render');
return (
<p>{this.props.number}</p>
)
}
}
//類組件可以用繼承
class Title extends PureComponent{
render(){
console.log('Title render');
return (
<p>{this.props.title}</p>
)
}
}
//函數組件可以和memo
const Title2 = React.memo(props=>{
console.log('Title2 render');
return <p>{props.title}</p>;
});
//memo的實現
function memo(func){
class Proxy extends PureComponent{
render(){
return func(this.props);
}
}
return Proxy;
}
//memo的另一種實現 接收一個函數組件
function memo2(Func){
class Proxy extends PureComponent{
render(){
return <Func {...this.props}/>
}
}
return Proxy;
}
~~~
### 3.2 PureComponent
~~~
import React from 'react';
function shallowEqual(obj1,obj2){
if(obj1 === obj2){
return true;
}
if(typeof obj1 != 'object' || obj1 === null ||typeof obj2 != 'object' || obj2 === null ){
return false;
}
let keys1 = Object.keys(obj1);
let keys2 = Object.keys(obj2);
if(keys1.length != keys2.length){
return false;
}
for(let key of keys1){
if(!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]){
return false;
}
}
return true;
}
export default class PureComponent extends React.Component{
isPureReactComponent = true
shouldComponentUpdate(nextProps,nextState){
return !shallowEqual(this.props,nextProps)||!shallowEqual(this.state,nextState)
}
}
~~~
## 5\. 長列表優化
* 用數組保存所有列表元素的位置,只渲染可視區內的列表元素,當可視區滾動時,根據滾動的offset大小以及所有列表元素的位置,計算在可視區應該渲染哪些元素
* [react-window](https://www.npmjs.com/package/react-window)
* [fixed-size](https://react-window.now.sh/#/examples/list/fixed-size)
* [react-virtualized](https://www.npmjs.com/package/react-virtualized)
~~~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>長列表優化</title>
<style>
*{
margin: 0;
padding: 0;
}
ul,li{
list-style: none;
}
</style>
</head>
<body>
<div id="container" style="height:150px;overflow:auto">
<ul id="list"></ul>
<div id="content-placeholder"></div>
</div>
<script>
const ITEM_HEIGHT = 30;
const ITEM_COUNT = 10;
window.onload = function() {
const container = document.querySelector("#container");
const containerHeight = container.clientHeight;
const list = document.querySelector("#list");
list.style.height = containerHeight+'px';
const visibleCount = Math.ceil(containerHeight / ITEM_HEIGHT);
const placeholder = document.querySelector("#content-placeholder");
list.appendChild(renderNodes(0, visibleCount));
placeholder.style.height = (ITEM_COUNT * ITEM_HEIGHT -containerHeight) + "px";
container.addEventListener("scroll", function() {
list.style.webkitTransform = `translateY(${container.scrollTop}px)`;
list.innerHTML = "";
const firstIndex = Math.floor(container.scrollTop / ITEM_HEIGHT);
list.appendChild(renderNodes(firstIndex, firstIndex + visibleCount));
});
};
function renderNodes(from, to) {
const fragment = document.createDocumentFragment();
for (let i = from; i < to; i++) {
const el = document.createElement("li");
el.style.height='30px';
el.innerHTML = i + 1;
fragment.appendChild(el);
}
return fragment;
}
</script>
</body>
</html>
~~~
~~~
import React, { Component, lazy, Suspense } from "react";
import ReactDOM from "react-dom";
import { FixedSizeList as List } from 'react-window';
const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);
const Container = () => (
<List
height={150}
itemCount={1000}
itemSize={35}
width={300}
>
{Row}
</List>
);
ReactDOM.render(<Container/>, document.querySelector("#root"));
~~~
~~~
import React, { Component, lazy, Suspense } from "react";
import ReactDOM from "react-dom";
//import { FixedSizeList as List } from 'react-window';
class List extends React.Component{
state = {start:1}
constructor(){
super();
this.containerRef = React.createRef();
}
componentDidMount(){
this.containerRef.current.addEventListener('scroll',()=>{
let scrollTop = this.containerRef.current.scrollTop;
let start = Math.floor(scrollTop/this.props.itemSize);//起始的索引
this.setState({start});
});
}
render(){
let {width,height,itemCount,itemSize} = this.props;
let containerStyle = {height,width,position:'relative',border:'1px solid red',overflow:'auto'};
let itemStyle = {height:itemSize,width:'100%',position:'absolute',left:0,top:0};
let render = this.props.children;
let children = [];
let size = Math.floor(height/itemSize)+1;//每頁的條數
for(let index=this.state.start;index<=this.state.start+size;index++){
let style = {...itemStyle,top:(index-1)*itemSize};
children.push(render({index,style}));
}
let topStyle = {width:'100%',height:itemSize*this.start};
return (
<div style={containerStyle} ref={this.containerRef}>
<div style={{width:'100%',height:itemSize*itemCount}}>
{children}
</div>
</div>
)
}
}
const Row = ({ index, style }) => (
<div key={index} style={style}>Row{index}</div>
);
const Container = () => (
<List
height={150}
itemCount={100}
itemSize={30}
width={300}
>
{Row}
</List>
);
ReactDOM.render(<Container/>, document.querySelector("#root"));
~~~
## 6\. react devtool
* [react-devtools](https://github.com/facebook/react-devtools)
* [profiler](http://react.html.cn/blog/2018/09/10/introducing-the-react-profiler.html)
* [react-flame-graph](https://react-flame-graph.now.sh/)
- 文檔簡介
- 基礎面試題【珠峰2019.8】
- P01_call,aplly區別
- P02_綜合面試題講解2-2
- P03_箭頭函數和普通函數區別-綜合面試題講解2-3
- P05_實現indexOf
- P06_綜合面試題講解2-6
- P07_URL解析題
- P08_原型題
- P09_圖片延時加載
- P10_正則-包含數字字母下劃線
- P11_綜合面試題講解2-11
- P12_英文字母加空格
- P13_數組扁平化并去重
- P14_模擬實現new
- P15_合并數組
- P16_定時器,打印012345
- P17_匿名函數輸出值問題
- P18_a在什么情況下打印輸出+1+1+1
- P19_對數組的理解
- P20_冒泡排序
- P21_插入排序
- P22_快速排序
- P23_銷售額存在對象中
- P24_求數組的交集
- P25_旋轉數組
- P26_ [函數柯理化思想]
- P27_ [柯理化函數的遞歸]
- 網絡協議【珠峰2019.6】
- TypeScript+Axios入門+實戰【珠峰2019.11】
- 1.數據結構
- 2.函數和繼承
- 3.裝飾器
- 4.抽象類-接口-泛型
- 05-結構類型系統和類型保護
- 06-類型變換
- AST-抽象語法樹
- React性能優化【珠峰2019.10】
- 1-react性能優化
- 2-react性能優化
- 3.react-immutable
- React Hooks【珠峰2019.12】
- 前端框架及項目面試
- 第07章 React 使用
- 7-1 React使用-考點串講
- 7-2 JSX基本知識點串講
- 7-3 JSX如何判斷條件和渲染列表
- 7-4 React事件為何bind this
- 7-5 React事件和DOM事件的區別
- 7-6 React表單知識點串講
- 7-7 React父子組件通訊
- 7-8 setState為何使用不可變值
- 7-9 setState是同步還是異步
- 7-10 setState合適會合并state
- 7-11 React組件生命周期
- 7-12 React基本使用-知識點總結和復習
- 7-13 React函數組件和class組件有何區別
- 7-14 什么是React非受控組件
- 7-15 什么場景需要用React Portals
- 7-16 是否用過React Context
- 7-17 React如何異步加載組件
- 7-18 React性能優化-SCU的核心問題在哪里
- 7-19 React性能優化-SCU默認返回什么
- 7-20 React性能優化-SCU一定要配合不可變值
- 7-21 React性能優化-PureComponent和memo
- 7-22 React性能優化-了解immutable.js
- 7-23 什么是React高階組件
- 7-24 什么是React Render Props
- 7-25 React高級特性考點總結
- 7-26 Redux考點串講
- 7-27 描述Redux單項數據流
- 7-28 串講react-redux知識點
- 7-29 Redux action如何處理異步
- 7-30 簡述Redux中間件原理
- 7-31 串講react-router知識點
- 7-32 React使用-考點總結
- 第08章 React 原理
- 8-1 React原理-考點串講
- 8-2 再次回顧不可變值
- 8-3 vdom和diff是實現React的核心技術
- 8-4 JSX本質是什么
- 8-5 說一下React的合成事件機制
- 8-6 說一下React的batchUpdate機制
- 8-7 簡述React事務機制
- 8-8 說一下React組件渲染和更新的過程
- 8-9 React-fiber如何優化性能
- 第09章 React 面試真題演練
- 9-1 React真題演練-1-組件之間如何通訊
- 9-2 React真題演練-2-ajax應該放在哪個生命周期
- 9-3 React真題演練-3-組件公共邏輯如何抽離
- 9-4 React真題演練-4-React常見性能優化方式
- 9-5 React真題演練-5-React和Vue的區別
- 第10章 webpack 和 babel
- 10-1 webpack考點梳理
- 10-2 webpack基本配置串講(上)
- 10-3 webpack基本配置串講(下)
- 10-4 webpack如何配置多入口
- 10-5 webpack如何抽離壓縮css文件
- 10-6 webpack如何抽離公共代碼和第三方代碼
- 10-7 webpack如何實現異步加載JS
- 10-8 module chunk bundle 的區別
- 10-9 webpack優化構建速度-知識點串講
- 10-11 happyPack是什么
- 10-12 webpack如何配置熱更新
- 10-13 何時使用DllPlugin
- 10-14 webpack優化構建速度-考點總結和復習
- 10-15 webpack優化產出代碼-考點串講
- 10-16 什么是Tree-Shaking
- 10-17 ES Module 和 Commonjs 的區別
- 10-18 什么是Scope Hostin
- 10-19 babel基本概念串講
- 10-20 babel-polyfill是什么
- 10-21 babel-polyfill如何按需引入
- 10-22 babel-runtime是什么
- 10-23 webpack考點總結和復習
- 10-24 webpack面試真題-前端代碼為何要打包
- 10-25 webpack面試真題-為何Proxy不能被Polyfill
- 10-26 webpack面試真題-常見性能優化方法