组件卸载前执行清理操作
在组件卸载前进行清理操作
日常应用中定时器是最典型的例子,比方在函数组件中 ,useEffect
钩子内返回的函数中做清理操作
function Test() { useEffect(() => { let timer = setInterval(() => { console.log("interval running"); }, 1000); return () => { clearInterval(timer); }; }, []); return <div>test</div>;}
例子中如果在Test组件卸载的时候不清理定时器,控制台会始终打印interval running
应用纯组件
纯组件会对组件输出数据进行浅层比拟,以后输出与上次输出雷同,组件不会渲染。浅层比拟和diff比起来小号更少的性能。diff会遍历整个virtualDom树。
在类组件中,继承PureComponent,函数组件中应用memo来实现纯组件。
类组件测试代码
class App extends Component { constructor() { super(); this.state = { name: "jake", }; } updateName() { setInterval(() => { this.setState({ name: "jake" }); }, 1000); } componentDidMount() { this.updateName(); } render() { return ( <> <NormalCom name={this.state.name} /> <PureCom name={this.state.name} /> </> ); }}class NormalCom extends Component { render() { console.log("normal"); return <div>{this.props.name}</div>; }}class PureCom extends PureComponent { render() { console.log("pure com"); return <div>{this.props.name}</div>; }}
函数组件测试代码
const ShowName = memo(function ShowName(props) { console.log("render..."); return <div>{props.name}</div>;});function App() { const [name] = useState("jake"); const [index, setIndex] = useState(0); useEffect(() => { setInterval(() => { setIndex((prev) => prev + 1); }, 1000); }, []); return ( <div> <ShowName name={name} /> </div> );}
console.log("render...");
只打印了一次
应用类组件中shouldComponentUpdate
shouldComponentUpdate
能够进行深层比拟。编写自定义比拟逻辑,当返回值为true时从新渲染组件,返回false阻止从新渲染。
函数原型shouldComponentUpdate(nextProps, nextState)
测试代码
export default class App extends React.Component { constructor() { super(); this.state = { name: "jake", age: 18 }; } componentDidMount() { setTimeout(() => { this.setState({ name: "jake", age: 18, }); }, 1000); } shouldComponentUpdate(nextProps, nextState) { console.log(nextState); if ( nextState.name !== this.state.name || nextState.age !== this.state.age ) { return true; } else { return false; } } render() { console.log("render...."); return ( <div> {this.state.name} {this.state.age} </div> ); }}
render办法内的打印只执行了一次
应用懒加载
应用懒加载能够缩小bundle文件大小,放慢加载速度
路由懒加载
const Home = lazy(() => import(/* webpackChunkName: "Home" */ "./Home"));const List = lazy(() => import(/* webpackChunkName: "List" */ "./List"));function App() { return ( <BrowserRouter> <Link to="/">首页</Link> <Link to="/list">列表页</Link> <Switch> <Suspense fallback={<div>loading</div>}> <Route path="/" component={Home} exact /> <Route path="/list" component={List} /> </Suspense> </Switch> </BrowserRouter> );}
network中加载文件在路由切换时加载不同bundle文件
也能够在判断语句中动静判断加载模块内容
应用Fragment防止额定标记
React组件中返回jsx如果有多个同级元素,多个同级元素必须要有一个独特的父级,在这种状况下通常都会在最外层增加一个div,然而这样这个div显得无意义,当无意义的标记增多,浏览器渲染累赘会加剧。此时能够应用Fragment
,它在渲染时不会被渲染为真正的dom元素,这种占位符也能够用<></>
代替
import {Fragment} from "react";function App() { return ( <div> <Fragment> <div>1</div> <div>2</div> </Fragment> <> <div>1</div> <div>2</div> </> </div> );}
不要应用内联函数定义
应用内联函数后,render办法每次运行都会创立该函数的新实例,导致React在运行Virtual DOM比对时,新旧函数比对不相等,导致React总是为元素绑定新的函数实例,而旧的函数实例要交给垃圾回收解决。应该在组件独自定义函数,将函数绑定给事件。
<input onChange={e=>this.setState({value:e.target.value})}/>// 改写为 valueChange = e =>{ ... } <input onChange={this.valueChange}/>
在构造函数中进行函数this绑定
在类组件中应用fn(){}这种形式定义函数,函数this默认指向undefined。也就是说函数外部的this指向须要被更正。能够在构造函数中对函数this指向进行更正,也能够在行内进行更正,两者看起来没有太大区别,然而对性能影响不同
// ------------形式一------------------------------------ constructor() { super(); this.state = { name: "jake", age: 18 }; // 构造函数只执行一次,所以函数this指向更正的代码也只执行一次,效率更高 this.handClick = this.handClick.bind(this) }// ------------形式二------------------------------------ {/* 函数属于援用数据类型,render办法每次执行都会调用bind办法生成新的函数 */} <button onClick={this.handClick.bind(this)}></button>
类组件中的箭头函数
在类组件中应用箭头函数不存在this指向问题,因为箭头函数自身不绑定this,箭头函数在this指向问题上占优势,同时也有不利一面。应用箭头函数时,函数被增加为类的实例属性,而不是原型数属性,如果组件被屡次重用,每个组件实例对象都将会有一个雷同的函数实例,升高函数实例的可重用性,造成资源节约。所以,更正函数外部this指向的最佳做法是在构造函数中应用bind进行绑定
防止应用内联款式
<div style ={{color:"red"}}>test</div>
应用内联style为元素增加款式,内联style会被编译为JS代码,color属性会被转化为等效CSS款式规定,通过JS代码将款式映射到元素身上,JS操作DOM十分慢,浏览器会耗费更多的工夫执行脚本和渲染UI,从而减少组件渲染工夫。举荐将CSS文件导入款式组件,能通过CSS间接做的事件不要通过JS去做。
优化条件渲染
频繁挂载和卸载组件是一件耗费性能的操作,在实际操作中,应该缩小组件挂载和卸载次数。
代码中经常出现须要利用一个变量的ture
和false
值来判断某个组件是否应该被渲染就是罕用案例。
return ( <> {show && <shouwComponent/>} <shouwComponentA/> <shouwComponentB/> </>)
防止反复有限渲染
class App extends React.Component { constructor() { super(); this.state = { name: "jake" }; } render() { this.setState({ name: "tony" }); return <div>{this.state.name}</div>; }}
在应用程序状态产生更改时,React会调用render办法,如果在render办法中持续更改应用程序状态,就会产生render办法递归调用导致利用报错。
下面例子报错不能再componentWillUpdate
componentDidUpdate
render
办法中调用setState
。源码中对反复调用做了限度,为50次。超过50次就报错。render办法应该被作为纯函数,在render办法中不要调用setState不要应用其余伎俩查问更改原生DOM元素,以及其余更改应用程序的任何操作。render办法的执行要依据状态的扭转,这样能够放弃组件的行为和渲染形式统一
为组件创立谬误边界
默认状况下,组件渲染谬误会导致整个程序中断,创立谬误边界能够确保在特定组件产生谬误时程序不会中断,减少程序的健壮性。
谬误边界是一个Reac组件,能够捕捉子组件在渲染时产生的谬误,当谬误产生时,能够将谬误记录下来,或者显示备用UI界面。谬误边界波及两个生命周期函数,getDerivedStateFromError
和componentDidCatch
getDerivedStateFromError
为静态方法,返回一个对象,返回对象会和state对象进行合并,用于更改应用程序状态,显示备用UI界面componentDidCatch
用于记录应用程序错误信息,参数就是谬误对象
谬误边界不能捕捉异步谬误,如:点击按钮产生的谬误
谬误边界类
import React from "react";import App from "./App";export default class ErrorBoundaries extends React.Component { constructor() { super(); this.state = { hasError: false, }; } componentDidCatch(error) { console.log("componentDidCatch"); console.log(error); } static getDerivedStateFromError() { console.log("getDerivedStateFromError"); return { hasError: true, }; } render() { if (this.state.hasError) { return <div>error occur</div>; } return <App />; }}
app类中抛出异样
function App() { throw new Error(" error happen"); return <div>app</div>;}
界面会打印出须要出现的预期谬误后果
防止数据结构渐变
组件中props和state数据结构应该放弃一致性,数据结构渐变会导致输入不统一,如果不留神这种问题,可能会呈现一些让人感到莫名的谬误,数据结构简单之后,排查起来也会减少难度。
优化依赖项大小
程序中常常会依赖第三方包,然而不想援用包中所有代码,只须要局部即可,此时能够应用插件对依赖项进行优化
yarn add react-app-rewired customize-cra lodash babel-plugin-lodash
react-app-rewired
笼罩create-react-app的默认配置customize-cra
导出一些辅助办法,让代码更为简洁babel-plugin-lodash
对利用中的lodash进行精简
没做任何操作时,打包大小为
1.引入lodash
,而后打包
import _ from "lodash"function App() { console.log(_.chunk(['a','b','c','d'],2)) return <div>app</div>;}
2,开始优化操作,根目录下新建config-overrides.js
override能够接管多个参数,每个参数都是一个配置函数,函数构造oldConfig,返回newConfig
useBabelRc:容许应用.babelrc文件进行babel配置
const { override, useBabelRc } = require("customize-cra");// eslint-disable-next-line react-hooks/rules-of-hooksmodule.exports = override(useBabelRc());
3,新建.babelrc
配置
{ "plugins": ["lodash"]}
4,批改package.json
配置
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test --env=jsdom", "eject": "react-scripts eject" },
而后打包
包体积减小