目录
1.2 shouldComponentUpdate / Pure Component
从类组件==>函数组件
1、memo
1.React核心开发团队一直都努力地让React变得更快。在React中可以用来优化组件性能的方法大概有以下几种:
组件懒加载: React.lazy(...) 和 < Suspense />
Pure Component
shouldComponentUpdate(...){...}生命周期函数
React16.6加入的另外一个专门用来优化函数组件(Functional Component)性能的方法: React.memo
1.1无用的渲染
组件是构成React视图的一个基本单元。
有些组件会有自己本地的状态(state), 当它们的值由于用户的操作而发生改变时,组件就会重新渲染。
在一个React应用中,一个组件可能会被频繁地进行渲染。这些渲染虽然有一小部分是必须的,不过大多数都是无用的,它们的存在会大大降低我们应用的性能
import React from 'react';
class TestC extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate')
}
render() {
console.log("render"运行了,重新生成了Vnode)
return (
<div >
{this.state.count}
//每次点击它,会做Diif算法,检测到没变化,就不改变页面,但是都会刷新页面
//每次都会运行render函数,这就是无用渲染
<button onClick={()=>this.setState({count: 1})}>Click Me</button>
</div>
);
}
}
export default TestC;
TestC组件有一个本地状态count,它的初始值是0(state = {count: 0})。当我们点击Click Me按钮时,count的值被设置为1。这时候屏幕的数字将会由0变成1。当我们再次点击该按钮时,count的值还是1, 这时候TestC组件不应该被重新渲染,可是现实不是这样的。
为了测试count重复设置相同的值组件会不会被重新渲染, 我为TestC组件添加了两个生命周期函数: componentWillUpdate和componentDidUpdate。componentWillUpdate方法在组件将要被重新渲染时被调用,而componentDidUpdate方法会在组件成功重渲染后被调用。
在浏览器中运行我们的代码,然后多次点击Click Me按钮,你可以看到打印了多次更新操作
所以即使count被设置相同的值,TestC组件还是会被重新渲染,这些就是所谓的无用渲染。
1.2 shouldComponentUpdate / Pure Component
为了避免React组件的无用渲染,我们可以实现自己的shouldComponentUpdate生命周期函数。
当React想要渲染一个组件的时候,它将会调用这个组件的shouldComponentUpdate函数, 这个函数会告诉它是不是真的要渲染这个组件。
其中各个参数的含义是:
nextProps: 组件将会接收的下一个参数props
nextState: 组件的下一个状态state
shouldComponentUpdate函数一直返回true,这就告诉React,无论何种情况都要重新渲染该组件。
这个方法的返回值是false,React永远都不会重新渲染我们的组件。
shouldComponentUpdate(nextProps, nextState) {
return true
}
shouldComponentUpdate(nextProps, nextState) {
return false
}
案例:
用shouldComponentUpdate重写TestC组件:
import React from 'react';
class TestC extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate')
}
shouldComponentUpdate(nextProps, nextState) {
if (this.state.count === nextState.count) {
return false
}
return true
}
render() {
return (
<div>
{ this.state.count }
<button onClick = {
() => this.setState({ count: 1 }) }> Click Me </button>
</div>
);
}
}
export default TestC;
1.3 Pure Component
React在v15.5的时候引入了Pure Component组件。
React在进行组件更新时,如果发现这个组件是一个PureComponent,它会将组件现在的state和props和其下一个state和props进行浅比较,
如果它们的值没有变化,就不会进行更新,就不会重新生成Vnode,不会再次运行render函数
使用Pure Component,只需要extends React.PureComponent
import React from 'react';
class TestC extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate')
}
//这是自己实现,如果数据多了,自己实现就比较麻烦
/*shouldComponentUpdate(nextProps, nextState) {
if (this.state.count === nextState.count) {
return false
}
return true
}*/
render() {
return (
<div>
{ this.state.count }
<button onClick = {() => this.setState({ count: 1 }) }> Click Me </button>
</div >
);
}
}
export default TestC;
1.4 React.memo
解决函数组件的无用渲染
React.memo()是React v16.6引进来的新属性。
它的作用和React.PureComponent类似,是用来控制函数组件的重新渲染的。React.memo(...)其实就是函数组件的React.PureComponent
函数组件也会有无用的渲染,但是还不能用Pure Component去解决,所以函数组件可以用来写一些不变的数据,比如logo的渲染
const Funcomponent = ()=> {
return (
<div>
I am a Funtional component
</div>
)
}
const MemodFuncComponent = React.memo(FunComponent)
//React.memo会返回一个纯化的组件MemoFuncComponent,这个组件将会在JSX标记中渲染出来。当组件的参数props和状态state发生改变时,React将会检查前一个状态和参数是否和下一个状态和参数是否相同,如果相同,组件将不会被渲染,如果不同,组件将会被重新渲染。
案例:
let TestC = (props) => {
console.log('Rendering TestC :', props)
return (
<div>
{ props.count }
</div>
)
}
TestC = React.memo(TestC)
//就这么简单 只想问一句 牛皮吗?
2、context
1.Context上下文
(其实跟vue中的provid-inject 的传值(父传孙)相似)
Context被翻译为上下文,在编程领域,这是一个经常会接触到的概念,React中也有
React文档官网并未对Context给出“是什么”的定义,更多是描述使用的Context的场景,以及如何使用Context,在React的官方文档中,Context被归类为高级部分,属于React的高级API,但官方并不建议在稳定版的App中使用Context。(因为爷爷、爸爸都可以作为提供者,所以就比较乱,所以在项目中要用这个技术的话,就引入仓库)
事实上,很多优秀的React组件都通过Context来完成自己的功能,比如react-redux的< Provider />,就是通过Context提供一个全局态的store,拖拽组件react-dnd,通过Context在组件中分发DOM的Drag和Drop事件,路由组件react-router通过Context管理路由状态等等。在React组件开发中,如果用好Context,可以让你的组件变得强大,而且灵活。
简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递
使用props或者state传递数据,数据自顶下流:
使用Context,可以跨越组件进行数据传递:
2.如何使用Context
如果要Context发挥作用,需要用到两种组件,一个是Context生产者(Provider),通常是一个父节点,另外是一个Context的消费者,通常是一个或者多个子节点。所以Context的使用基于生产者消费者模式。
对于父组件,也就是Context生产者,需要通过一个静态属性childContextTypes声明提供给子组件的Context对象的属性,并实现一个实例getChildContext方法,返回一个代表Context的纯对象 。
ThemeContext.jsx (这个创建上下文 不能直接写在爷爷组件中,必须要在外面写一个文件,只创建一个上下文)
import React from 'react';
const ThemeContext = React.createContext({
background: 'red',
color: 'white'
});
export default ThemeContext;
//通过静态方法React.createContext()创建一个Context对象,这个Context对象包含两个组件,<Provider />和<Consumer />
爷爷组件App
import React from 'react';
import ThemeContext from './ThemeContext.js';
import Header from './Header.js';
class App extends React.Component {
render () {
return (
<ThemeContext.Provider value={{background: 'green', color: 'white'}}>
<Header > </Header >
</ThemeContext.Provider>
);
}
}
爸爸组件Header
import React from 'react';
import Title from './Title.js';
class Header extends React.Component {
render () {
return (
<Title>Hello React Context API</Title>
);
}
}
export default Header;
孙组件:Title
import React from 'react';
import ThemeContext from './ThemeContext.js';
class Title extends React.Component {
render () {
return (
<ThemeContext.Consumer>
{context => (
<h1 style={{background: context.background, color: context.color}}>
{this.props.children}
</h1>
)}
</ThemeContext.Consumer>
);
}
}
Title.contextType = ThemeContext
export default Title;
//<Consumer />的children必须是一个函数,通过函数的参数获取<Provider />提供的Context
3.可以直接获取Context的地方
注意:接受值的后代组件名.contextType = ThemeContext
(就是:在导出组件之前,给组件写一个contextType属性的等于自己定义的那个上下文ThemeContext)
==> 孙子.contextType=ThemeContext
实际上,除了实例的context属性(this.context),React组件还有很多个地方可以直接访问父组件提供的Context
类组件:
constructor(props, context)
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componetWillUpdate(nextProps, nextState, nextContext)
所有能访问this的地方都可以==>this.context
无状态的函数组件:
const StatelessComponent = (props, context) => (
)