react性能优化

108 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

前面的两天我们了解到了性能的主要损失在于不必要的渲染,所以我们通过memo,继承PureComponent,重写shouldComponentUpdate,提升为全局函数等等方法来进行性能的优化,今天再从代码结构编码的结构上来讲讲性能优化。

场景复现

我们知道react是根据组件的stateprops来进行判断的,很多时候我们如果出现子父组件的层次,在父组件更新的时候子组件也会一起重新渲染。你这肯定觉得不可思议,既然都是子父组件了,那么一定就是父组件传递给子组件的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;

1.gif

可以看到每次父组件的更新都引起了子组件的重新渲染,解决的方法也是很简单可以用我们前面见过的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>
  );
};

2.gif

可以看到优化结构后的代码就不会出现无效的渲染了。

总结

合理的代码结构也是性能优化的关键。