持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
前面的两天我们了解到了性能的主要损失在于不必要的渲染,所以我们通过memo,继承PureComponent,重写shouldComponentUpdate,提升为全局函数等等方法来进行性能的优化,今天再从代码结构编码的结构上来讲讲性能优化。
场景复现
我们知道react是根据组件的state和props来进行判断的,很多时候我们如果出现子父组件的层次,在父组件更新的时候子组件也会一起重新渲染。你这肯定觉得不可思议,既然都是子父组件了,那么一定就是父组件传递给子组件的props进行了更改导致的渲染,所以子组件进行重新渲染,好像没有什么问题呀?其实并非如此,在有些场景下,我们也可能产生无联系的父子组件嵌套,如一些无状态的公共UI组件和我们在使用context的时候。
场景一:
import React, {
useContext,
createContext,
useState,
PureComponent,
Component,
memo,
} from "react";
const Other = () => {
return (
<div>
<h2>Other</h2>
<h3>{Math.random()}</h3>
</div>
);
};
const App = () => {
const [userType, setuserType] = useState({ userType: "学生" });
//重新登录 改变用户类型
function relogin() {
if (userType.userType === "学生") {
setuserType({ userType: "老师" });
} else {
setuserType({ userType: "学生" });
}
}
return (
<div>
<h1>我的用户类型是:{userType.userType}</h1>
<button onClick={relogin}>点我重新登录</button>
<Other></Other>
</div>
);
};
export default App;
可以看到每次父组件的更新都引起了子组件的重新渲染,解决的方法也是很简单可以用我们前面见过的memo等方法,如果不用这些缓存技术可以么?可以的!我们可以从结构上进行优化。
优化
import React, {
useContext,
createContext,
useState,
PureComponent,
Component,
memo,
} from "react";
const Other = () => {
return (
<div>
<h2>Other</h2>
<h3>{Math.random()}</h3>
</div>
);
};
const Type=({children})=>{
const [userType, setuserType] = useState({ userType: "学生" });
//重新登录 改变用户类型
function relogin() {
if (userType.userType === "学生") {
setuserType({ userType: "老师" });
} else {
setuserType({ userType: "学生" });
}
}
return (
<div>
<h1>我的用户类型是:{userType.userType}</h1>
<button onClick={relogin}>点我重新登录</button>
{children}
</div>
)
}
const App = () => {
return (
<div>
<Type>
<Other></Other>
</Type>
</div>
);
};
export default App;
我们将state进行了分离,再通过props.children来进行父子组件的嵌套就可以实现我们的需求。为什么呢?由于我的Other是以props的形式传入的,在父组件渲染的时候直接复用了原来不变的props,而不是重置整个渲染函数,原先不是以props的形式传入,那么每次函数的执行都是一个新的函数,里面的子组件也是重新创建的,现在类似提取出来作为全局组件,类似闭包的引用,那么就不会进行更改了。
context场景
在context的使用中我们会更经常遇到这样的需求,由于context的出现就是为了给许多子组件提供统一的状态源,那么可能就会出现有些子组件并不是consumer的情况,但是由于你代码结构的不合理又没有使用合适的缓存策略,使得子组件出现了很多无效的重渲染。
import React, {
useContext,
createContext,
useState,
PureComponent,
Component,
memo,
} from "react";
const UserType = createContext({ userType: "学生" });
const App = () => {
const [userType, setuserType] = useState({ userType: "学生" });
//重新登录 改变用户类型
function relogin() {
if(userType.userType==='学生'){
setuserType({ userType: "老师" });
}else{
setuserType({ userType: "学生" });
}
}
return (
<div>
<UserType.Provider value={{ userType, relogin }}>
<Content></Content>
<Other></Other>
</UserType.Provider>
</div>
);
};
const Content = () => {
const context = useContext(UserType);
return (
<div>
<h1>我的用户类型是:{context.userType.userType}</h1>
<button onClick={context.relogin}>点我重新登录</button>
<h1>Content:{Math.random()}</h1>
</div>
);
};
const Other = () => {
return (
<div>
<h2>Other</h2>
<h3>{Math.random()}</h3>
</div>
);
};
export default App;
如上就是一个常见的问题场景Other组件并不是所谓的consumer组件,但是会在每次父组件重渲染的时候一起渲染,这是不合理的,我们可以给Other加上memo进行缓存,也可以进行结构的改变,让Other组件作为props的形式传入,这样就不会产生无效的渲染了。
import React, {
useContext,
createContext,
useState,
PureComponent,
Component,
memo,
} from "react";
const UserType = createContext({ userType: "学生" });
const App = () => {
return (
<div>
<Provider>
<Other></Other>
</Provider>
</div>
);
};
const Provider = ({ children }) => {
const [userType, setuserType] = useState({ userType: "学生" });
//重新登录 改变用户类型
function relogin() {
if (userType.userType === "学生") {
setuserType({ userType: "老师" });
} else {
setuserType({ userType: "学生" });
}
}
return (
<div>
<UserType.Provider value={{ userType, relogin }}>
<Content></Content>
{children}
</UserType.Provider>
</div>
);
};
const Content = () => {
const context = useContext(UserType);
return (
<div>
<h1>我的用户类型是:{context.userType.userType}</h1>
<button onClick={context.relogin}>点我重新登录</button>
<h1>Content:{Math.random()}</h1>
</div>
);
};
const Other = () => {
return (
<div>
<h2>Other</h2>
<h3>{Math.random()}</h3>
</div>
);
};