[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面试真题-常见性能优化方法