React 过渡动画
- 在 React 中我们可以通过原生的 CSS 来实现过渡动画
- 但是 React 社区为我们提供了
react-transition-group
帮助我们快速过渡动画
import React from 'react'; import styled from 'styled-components'; const StyleDiv = styled.div` width: ${props => props.width}; height: ${props => props.height}; background: skyblue; opacity: ${props => props.opacity}; transition: all 3s; ` class App extends React.Component { constructor(props) { super(props); this.state = { width: 0, height: 0, opacity: 0 } } render() { return ( <div> <StyleDiv {...this.state}/> <button onClick={() => { this.btnClick() }}>按钮 </button> </div> ); } btnClick() { this.setState({ width: '100px', height: '100px', opacity: 1 }) } } export default App;
性能优化
嵌套组件的 render 调用
- 默认情况下, 只要父组件 render 被调用, 那么所有的后代组件的 render 也会被调用
当前存在的问题
- 如果我们只修改了父组件的数据, 并没有修改子组件的数据, 并且子组件中也没有用到父组件中的数据
- 那么子组件还是会重新渲染, 子组件的 render 方法还是会重新执行, 这样就带来了性能问题
App.js:
import React from "react"; class Home extends React.Component { constructor(props) { super(props); this.state = { age: 18 } } render() { console.log('Home-render-被调用'); return ( <div> <p>{this.state.age}</p> </div> ); } } class App extends React.Component { constructor(props) { super(props); this.state = { name: 'yangbuyiya' } } render() { console.log('App-render-被调用'); return ( <div> <p>{this.state.name}</p> <button onClick={() => { this.btnClick() }}>APP按钮 </button> <Home/> </div> ); } btnClick() { this.setState({ name: 'Jonathan_Lee' }); } } export default App;
可以在 shouldcomponentupdate
该生命周期函数当中进行决定是否需要进行重新渲染,官方文档:https://zh-hans.reactjs.org/docs/react-component.html#shouldcomponentupdate
修改 App.js:
import React from "react"; class Home extends React.Component { constructor(props) { super(props); this.state = { age: 18 } } shouldComponentUpdate(nextProps, nextState, nextContext) { return this.state.age !== nextState.age; } render() { console.log('Home-render-被调用'); return ( <div> <p>{this.state.age}</p> </div> ); } } class App extends React.Component { constructor(props) { super(props); this.state = { name: 'yangbuyiya' } } render() { console.log('App-render-被调用'); return ( <div> <p>{this.state.name}</p> <button onClick={() => { this.btnClick() }}>APP按钮 </button> <Home/> </div> ); } btnClick() { this.setState({ name: 'Jonathan_Lee' }); } } export default App;
shouldComponentUpdate 存在的问题
- 所有需要优化的子组件都需要实现这个方法, 但这个方法并没有技术含量
解决方法
- 让组件继承于
PureComponent
, 让 React 自动帮我们实现
App.js:
import React from "react"; class Home extends React.PureComponent { constructor(props) { super(props); this.state = { age: 18 } } render() { console.log('Home-render-被调用'); return ( <div> <p>{this.state.age}</p> </div> ); } } class App extends React.Component { constructor(props) { super(props); this.state = { name: 'yangbuyiya' } } render() { console.log('App-render-被调用'); return ( <div> <p>{this.state.name}</p> <button onClick={() => { this.btnClick() }}>APP按钮 </button> <Home/> </div> ); } btnClick() { this.setState({ name: 'Jonathan_Lee' }); } } export default App;
关于函数式组件的优化方案
对于函数式组件来说:
- 没有继承关系
- 没有生命周期方法
函数组件的性能优化
对于类组件, 我们可以通过实现 shouldComponentUpdate
方法, 或者继承于 PureComponent
, 来解决性能的优化问题, 但是对于函数式组件, 是没有生命周期的, 是没有继承关系的,那么在函数式组件中如何解决性能优化问题呢?当然是有的,在 React 当中可以通过 React.memo()
高阶函数来定义函数式组件,React.memo()
会返回一个优化后的组件给我们。
App.js:
import React from "react"; const PurHome = React.memo(function () { console.log('Home-render-被调用'); return ( <div> <p>Home</p> </div> ) }); class App extends React.Component { constructor(props) { super(props); this.state = { name: 'yangbuyiya' } } render() { console.log('App-render-被调用'); return ( <div> <p>{this.state.name}</p> <button onClick={() => { this.btnClick() }}>APP按钮 </button> <PurHome/> </div> ) } btnClick() { this.setState({ name: 'Jonathan_Lee' }) } } export default App;
state 注意点
- 永远不要直接修改
state
中的数据 - 如果要修改
state
中的数据, 必须通过setState
传递一个新的值
首先来看一个两种不同写法的运行结果吧,第一种就是直接进行修改不通过 setState 进行修改:
App.js:
import React from "react"; class App extends React.PureComponent { constructor(props) { super(props); this.state = { age: 0 } } render() { console.log('App-render-被调用'); return ( <div> <p>{this.state.age}</p> <button onClick={() => { this.btnClick() }}>APP按钮 </button> </div> ) } btnClick() { this.state.age = this.state.age + 1; this.setState(this.state); } } export default App;
运行如上代码会发现,页面没有进行重新渲染,就算继承了 PureComponent 也不会进行重新渲染,因为它的底层实现我们在如上的几个代码片段已经实现过了,就算比较当前的值是否和下一次的值是否不同如果不同就重新渲染但是,如上的这种设置方式就会造成两个值是相同的就不会再重新渲染页面。
第二种通过 setState 进行修改:
App.js:
import React from "react"; class App extends React.PureComponent { constructor(props) { super(props); this.state = { age: 0 } } render() { console.log('App-render-被调用'); return ( <div> <p>{this.state.age}</p> <button onClick={() => { this.btnClick() }}>APP按钮 </button> </div> ) } btnClick() { this.setState({ age: this.state.age + 1 }); } } export default App;
如上之所以可以重新渲染是因为通过 setState 设置值就会触发 React 当中的重新渲染机制,在 PureComponent 底层实现比较的原理比较也是不同的两个值,也会触发页面的更新。
以上两种写法的区别:
- 第一种这种方式是设置了以前的对象
- 第二种方式是设置了一个新的对象
最后
本期结束咱们下次再见👋~
🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗