第127题 在实际工作中,你对React做过哪些优化
- 修改CSS模拟v-show
// 原始写法
{!flag && <MyComonent style={{display:'none'}} />}
{flag && <MyComonent />}
// 模拟v-show
{<MyComonent style={{display:flag ? 'block' : 'none'}} />}
- 循环使用key
key不要用index
- 使用Flagment或 <></>空标签包裹减少多个层级组件的嵌套
- jsx中不要定义函数 :
JSX会被频繁执行的
// bad
// react中的jsx被频繁执行(state更改)应该避免函数被多次新建
<button onClick={()=>{}}>点击</button>
// goods
function useButton() {
const handleClick = ()=>{}
return <button onClick={handleClick}>点击</button>
}
- 使用shouldComponentUpdate
- 判断组件是否需要更新
- 或者使用
React.PureComponent比较props第一层属性 - 函数组件使用
React.memo(comp, fn)包裹function fn(prevProps,nextProps) {// 自己实现对比,像shouldComponentUpdate}
- Hooks缓存数据和函数
useCallback: 缓存回调函数,避免传入的回调每次都是新的函数实例而导致依赖组件重新渲染,具有性能优化的效果useMemo: 用于缓存传入的props,避免依赖的组件每次都重新渲染
- 使用异步组件
import React,{lazy,Suspense} from 'react'
const OtherComp = lazy(/**webpackChunkName:'OtherComp'**/ ()=>import('./otherComp'))
function MyComp(){
return (
<Suspense fallback={<div>loading...</div>}>
<OtherComp />
</Suspense>
)
}
- 路由懒加载
import React,{lazy,Suspense} from 'react'
import {BrowserRouter as Router,Route, Switch} from 'react-router-dom'
const Home = lazy(/**webpackChunkName:'h=Home'**/()=>import('./Home'))
const List = lazy(/**webpackChunkName:'List'**/()=>import('./List'))
const App = ()=>(
<Router>
<Suspense fallback={<div>loading...</div>}>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/list' component={List} />
</Switch>
</Suspense>
</Router>
)
- 使用SSR :
Next.js
连环问:你在使用React时遇到过哪些坑
- 自定义组件的名称首字母要大写
// 原生html组件
<input />
// 自定义组件
<Input />
- JS关键字的冲突
// for改成htmlFor,class改成className
<label htmlFor="input-name" className="label">
用户名 <input id="username" />
</label>
- JSX数据类型
// correct
<Demo flag={true} />
// error
<Demo flag="true" />
setState不会马上获取最新的结果
- 如需要实时获取结果,在回调函数中获取
setState({count:this.state.count + 1},()=>console.log(this.state.count)}) setState在合成事件和生命周期钩子中,是异步更新的- 在原生事件 和
setTimeout中,setState是同步的,可以马上获取更新后的值; - 原因: 原生事件是浏览器本身的实现,与事务流无关,自然是同步;而
setTimeout是放置于定时器线程中延后执行,此时事务流已结束,因此也是同步;
- 如需要实时获取结果,在回调函数中获取
// setState原理模拟
let isBatchingUpdate = true;
let queue = [];
let state = {number:0};
function setState(newSate){
//state={...state,...newSate}
// setState异步更新
if(isBatchingUpdate){
queue.push(newSate);
}else{
// setState同步更新
state={...state,...newSate}
}
}
// react事件是合成事件,在合成事件中isBatchingUpdate需要设置为true
// 模拟react中事件点击
function handleClick(){
isBatchingUpdate=true; // 批量更新标志
/**我们自己逻辑开始 */
setState({number:state.number+1});
setState({number:state.number+1});
console.log(state); // 0
setState({number:state.number+1});
console.log(state); // 0
/**我们自己逻辑结束 */
state= queue.reduce((newState,action)=>{
return {...newState,...action}
},state);
}
handleClick();
console.log(state); // 1
// setState笔试题考察 下面这道题输出什么
class Example extends React.Component {
constructor() {
super()
this.state = {
val: 0
}
}
// componentDidMount中isBatchingUpdate=true setState批量更新
componentDidMount() {
this.setState({ val: this.state.val + 1 }) // 添加到queue队列中,等待处理
console.log(this.state.val)
// 第 1 次 log
this.setState({ val: this.state.val + 1 }) // 添加到queue队列中,等待处理
console.log(this.state.val)
// 第 2 次 log
setTimeout(() => {
// 在原生事件和setTimeout中(isBatchingUpdate=false),setState同步更新,可以马上获取更新后的值
this.setState({ val: this.state.val + 1 }) // 同步更新
console.log(this.state.val)
// 第 3 次 log
this.setState({ val: this.state.val + 1 }) // 同步更新
console.log(this.state.val)
// 第 4 次 log
}, 0)
}
render() {
return null
}
}
// 答案:0, 0, 2, 3
