手机网站建设口碑好,自己做网站商城需要营业执照吗,北京网站开开发公司电话,最好免费高清视频在线观看目录
一、不使用任何性能优化API进行优化
二、通过性能优化API优化
1、React.memo
2、useCallback
3、useMemo
4、PureComponent
三、总结 总览#xff1a;react的优化核心思想就是让react跳过重新渲染那个些没有改变的Component#xff0c;而只重新渲染发生变化的C…目录
一、不使用任何性能优化API进行优化
二、通过性能优化API优化
1、React.memo
2、useCallback
3、useMemo
4、PureComponent
三、总结 总览react的优化核心思想就是让react跳过重新渲染那个些没有改变的Component而只重新渲染发生变化的Component。 所以变化都是围绕这三种变量展开的props、state、context 。
一、不使用任何性能优化API进行优化
核心思想将变和不变的部分分离开。
案例一没有变化的组件被多次渲染了
function App() {const [num, updateNum] useState(0)return (div classNameAppinput typetext onChange{(e) updateNum(e.target.value)}/pnum is {num}/pExpensiveCpn//div);
}const ExpensiveCpn () {let now performance.now()while (performance.now() - now 100) {}console.log(组件耗时 render)return div耗时组件/div
}
运行结果可以看到在上面代码的情况下高耗能组件虽然没有被修改但每次修改state里的数据还是都会重新渲染改组件。 下面对其进行优化即变的部分组件抽离
function App() {return (div classNameAppInput/ExpensiveCpn//div);
}const Input () {const [num, updateNum] useState(0)return (input typetext onChange{(e) updateNum(e.target.value)}/pnum is {num}/p/)
}const ExpensiveCpn () {let now performance.now()while (performance.now() - now 100) {}console.log(组件耗时 render)return div耗时组件/div
}
页面效果我们将页面变的部分和不变的部分进行抽离后实现效果变的组件发生变化时不会影响我们的其他组件了。 如果你的代码结果写的好你几乎可以不用性能优化API。
案例二与案例一类似不同在于这里父组件也用到了state里的变量。
function App() {const [num, updateNum] useState(0)return (div classNameApp title{num }input typetext onChange{(e) updateNum(e.target.value)}/pnum is {num}/pExpensiveCpn//div);
}const ExpensiveCpn () {let now performance.now()while (performance.now() - now 100) {}console.log(组件耗时 render)return div耗时组件/div
}
这段代码和案例一没有优化前的效果一样。
下面我们将对其进行优化
function App() {return (InputWrapperExpensiveCpn//InputWrapper);
}function InputWrapper({children}) {const [num, updateNum] useState(0)return (div classNameApp title{num }input typetext onChange{(e) updateNum(e.target.value)}/pnum is {num}/p{children}/div)
}const ExpensiveCpn () {let now performance.now()while (performance.now() - now 100) {}console.log(组件耗时 render)return div耗时组件/div
}
页面效果可以看到通过{children}插槽可以实现父元素也有变量时的优化。 从以上两个案例可以得出以下结论
当父组件满足性能优化条件时子孙组件可能命中性能优化。
本质是将变的部分和不变的部分分离。 二、通过性能优化API优化
为什么需要性能优化API
当子孙结点的父结点未命中性能优化时父结点的分支也将不会命中性能优化。
1、React.memo
官方文档memo – React 中文文档
React.memo是一个高阶组件它接收另一个组件作为参数会返回一个包装过的新组件包装后的新组件就会具有缓存作用。
包装后只有组件的props发生变化才会触发组件的重新渲染否则总是返回缓存中的结果。
案例一 function App() {console.log(App发生渲染)const [count, setCount] useState(1)return (div classNameAppSon/p{count}/pbutton onClick{() setCount(count count)}点击加1/button/div/);
}const Son () {console.log(Son发生渲染)return divpson/pGrandSon//div
}const GrandSon () {console.log(GrandSon发生渲染)return divpGrandSon/p/div
} 实现效果每次修改App里的数据子孙组件都发生了改变。 案例二
对案例一进行优化
function App() {console.log(App发生渲染)const [count, setCount] useState(1)return (div classNameAppSon/p{count}/pbutton onClick{() setCount(count count)}点击加1/button/div/);
}const Son React.memo(() {console.log(Son发生渲染)return divpson/pGrandSon//div
})const GrandSon () {console.log(GrandSon发生渲染)return divpGrandSon/p/div
}
实现效果可以看到当Son组件被memo包裹时Son一系列数下的组件都被缓存优化到了。每次修改父组件子孙组件都未重新渲染。 案例三
在App组件中像子组件Son传入修改参数的setCount函数。
function App() {console.log(App发生渲染)const [count, setCount] useState(1)const addOne () {setCount(count count)}return (div classNameAppSon addOne{addOne}/p{count}/pbutton onClick{addOne}点击加1/button/div/);
}const Son React.memo(({addOne}) {console.log(Son发生渲染)return divpson/pbutton onClick{addOne}Son点击加1/buttonGrandSon//div
})const GrandSon () {console.log(GrandSon发生渲染)return divpGrandSon/p/div
}
实现效果当Son组件中调用了父组件中的函数时你会发现React.memo失效了此时子孙组件仍然会被重新渲染。因为App组件发生改变重新渲染addOne函数也会重新定义此时传入Son组件里的函数就相当于更新了会使得Son进行重新渲染。但问题是Son里的内容并没有改变有什么方法可以解决这种情况的问题呢这就要用到下面介绍的useCallback钩子了。 2、useCallback useCallback是一个钩子函数用来创建React中的回调函数。创建的回调函数不会总在组件重新渲染时重新创建。简单来说就是对回调函数做了一层缓存。
const cachedFn useCallback(fn, dependencies)
useCallback的第一个参数是一个回调函数第二个参数是依赖数组当依赖数组中的变量发生变化时回调函数才会重新创建如果不指定依赖数组回调函数每次都会重新创建失去意义。
注如果使用时不传第二个参数函数仍然会在每次渲染时重新创建和没使用没什么区别。
案例四
function App() {console.log(App发生渲染)const [count, setCount] useState(1)const addOne useCallback(() {setCount(count count)},[]) // 这里的函数只会在组件初始化时创建更新时不会再次创建return (div classNameAppSon addOne{addOne}/p{count}/pbutton onClick{addOne}点击加1/button/div/);
}const Son React.memo(({addOne}) {console.log(Son发生渲染)return divpson/pbutton onClick{addOne}Son点击加1/buttonGrandSon//div
})const GrandSon () {console.log(GrandSon发生渲染)return divpGrandSon/p/div
}
实现效果React.memo案例三中的问题解决了 案例五
function App() {console.log(App发生渲染)const [count, setCount] useState(1)const [num, setNum] useState(1)const addOne useCallback(() {setCount(count count num)setNum(num num 1)}, [num])return (div classNameAppSon addOne{addOne}/p{count}/pbutton onClick{addOne}点击加1/button/div/);
}const Son React.memo(({addOne}) {console.log(Son发生渲染)return divpson/pbutton onClick{addOne}Son点击加1/buttonGrandSon//div
})const GrandSon () {console.log(GrandSon发生渲染)return divpGrandSon/p/div
}
实现效果在依赖数组中数组callback函数中使用到的变化每当数组中的变量发生改变时回调函数都会重新定义执行时机和useEffect依赖数组里是类似的。但案例中这种情况已经失去了优化的初衷。所以案例五中的情况尽量不要用useCallback了使用和不使用效果都一样还多了一层缓存的耗时操作。 3、useMemo 相当于Vue里的Computed
与useCallback十分相似useCallback是用来缓存函数对象useMemo是用来缓存函数的执行结果。
不使用useMemo的执行效果
const sum (a, b) {console.log(求和执行了)return a b
}function App() {console.log(sum(1,2))console.log(sum(1,2))return (div/div/);
}
效果每次调用都会重新执行。 使用了useMemo:
const sum (a, b) {console.log(求和执行了)return a b
}function App() {const result React.useMemo(() {return sum(1, 2)})console.log(result)console.log(result)return (div/div/);
}
效果函数只执行了一次对于开销很大的函数使用useMemo可以很好地改善性能。 当useMemo里传入变量时
const sum (a, b) {console.log(求和执行了)return a b
}function App() {const [count, setCount] useState(1)let b 2// 每次组件渲染时都会执行// useMemo用于缓存函数的执行结果const result React.useMemo(() {return sum(count, b)}, [])useEffect(() {console.log(count:, count)console.log(result)console.log(result)}, [count])return (divbutton onClick{() setCount(prev prev 1)}a加1/button/div/);
}
打印可以看到每次执行都是第一次的结果变量改变也没有重新缓存result。 解决上面问题的方法 const result React.useMemo(() { return sum(count, b) }, [count]) // 第二个参数数组中传入对应变量当变量发生变化时会重新调用useMemo进行结果缓存更新。 对于上述情况如果修改过于频繁就基本使用不到缓存效果这种情况不推荐使用useMemo。
另外useMemo也可以像React.memo一样返回组件缓存
function App() {const el useMemo(() {return divphello/p/div}, [])return (div{el}/div/);
}
4、PureComponent
React.memo的对应。
PureComponent 会对 props 和 state 进行浅层比较。如果它们没有变化组件将不会重新渲染。 示例 以下是一个在类组件中使用 PureComponent 的示例包括数据传递和更新
import React, { PureComponent } from react;class MyComponent extends PureComponent {// 构造函数初始化状态constructor(props) {super(props);this.state {count: 0,name: Initial Name,};}// 处理点击事件更新状态handleClick () {// 示例 1更新数字状态this.setState({ count: this.state.count 1 });// 示例 2更新字符串状态如果 name 是从父组件传递的 props 且未变化不会触发重新渲染// 假设 name 是从父组件传递的 props以下更新不会触发重新渲染如果 name 未变化// this.setState({ name: this.props.name });};render() {return (divpCount: {this.state.count}/ppName: {this.state.name}/pbutton onClick{this.handleClick}Increment Count/button/div);}
}// 父组件
class ParentComponent extends React.Component {constructor(props) {super(props);this.state {name: Parent Name,};}handleNameChange () {this.setState({ name: Updated Name });};render() {return (divMyComponent name{this.state.name} /button onClick{this.handleNameChange}Change Name/button/div);}
}export default ParentComponent;在这个例子中
MyComponent 是一个继承自 PureComponent 的类组件。它有一个 count 状态用于数字的递增展示还有一个 name 状态也可以是从父组件传递的 props用于展示字符串。
在 render 方法中展示了 count 和 name 的值并有一个按钮用于触发 count 的递增。
ParentComponent 是父组件它有一个 name 状态并将其传递给 MyComponent。还有一个按钮用于更改 name 的状态。
PureComponent 会对 props 和 state 进行浅层比较。如果 props 或 state 的引用没有变化组件将不会重新渲染。在上面的例子中如果 MyComponent 接收到的 props.name 没有变化并且 state 中的 count 没有更新MyComponent 就不会重新渲染。
注意事项
PureComponent 的浅层比较对于基本数据类型如数字、字符串、布尔值是有效的但对于复杂数据类型如对象、数组它只会比较引用。如果对象或数组的内容发生变化但引用不变PureComponent 可能不会检测到变化。在这种情况下可以使用 immutable.js 或手动在 shouldComponentUpdate 中进行深层比较。 如果组件的 props 或 state 变化频繁且计算成本不高或者需要进行深层比较可能不需要使用 PureComponent。
三、总结
性能优化三部曲
1、寻找项目中性能损耗严重的子树
2、在子树的根节点使用性能优化API
3、子树中运用变与不变分离原则。
总结到此相信你已经掌握了性能优化的精髓。