Вероятно, ваши функциональные компоненты реакции никогда не оптимизировались.

React.js

Автор этой статьи: IMWeb lhyt Исходный источник:Сообщество IMWebВоспроизведение запрещено без согласия

До версии 16.6 функциональные компоненты не имели одинаковогоshouldComponentUpdateНет такого методаPureComponentЭто решение не может избежать повторного выполнения всего кода в функциональном компоненте, и оно должно контролироваться внешним условным рендерингом или компонентами более высокого порядка. В прошлом в случае выбора функциональных компонентов использовались относительно простые и относительно чистые компоненты, которые отвечали только за отображение. И конечным результатом компиляции babel для функционального компонента является то, что он выполняет толькоcreateElementЭтот шаг; у компонента класса также есть жизненный цикл, который нужно создать, и он все еще очень длинный, когда он, наконец, проходит через Babel в код es5.

React.memo

Когда вышел меморандум 16.6, функциональные компоненты имели что-то вродеPureComponentа такжеshouldComponentUpdateРешение, использование мемо:

const C = (props) => {
    return <section>那一夜{props.name}真美</section>
}

export default React.memo(C)

Когда родительский компонент выполняет рендеринг, нельзя избежать рендеринга компонента C и выполнения функции C (если снаружи не добавлено никакое суждение:{isShowC && <C />}). Когда дело доходит до компонента C, он поверхностно сравнивает значения реквизита до и после компонента C. еслиprops каждое значение свойства одинаково, пропустит выполнение функционального компонента C, уменьшит ненужный рендеринг и достигнет оптимизации производительности.

Второй параметр мемо

Второй параметр — это функция.Входящие параметры функции — это новые реквизиты и последние реквизиты.Мы можем сделать логическую оценку в функции и контролировать возвращаемое значение. Когда мы позволяем функции возвращать true, мы сообщаем, что эти два реквизита одинаковы, и нам не нужно повторно выполнять весь компонент функции; в противном случае, когда он будет ложным, компонент будет выполнен повторно.

memo(IfEqual, () => false);

Например, в этой строке кода функция оценки всегда возвращает false,memoзавернутыйIfEqualКомпонент будет повторно выполнен независимо от того, что

Когда мы используем заметки, мы можем оптимизировать их в соответствии с бизнесом:

React.memo(C, (nextProps, prevProps) => {
    // 做我们想做的事情,类似shouldComponentUpdate
})

Когда значение реквизита, переданное в функциональном компоненте, является функцией

Все мы знаем, что функции в js — это не простые типы данных, то естьfunction(){}а такжеfunction(){}не то же самое, что{}а также{}Не то же самое. Затем мы проходим вprops.onClick(даже если выглядит одинаково, содержание точно такое же), до и послеprops.onClickнельзя равнять

    <div>
      <IfEqual onClick={() => {}} />
    </div>

Я думаю, что встроенная функция выглядит некрасиво, поэтому, если вы определите ее раньше, вы не сможете избежать того же самого: они разные. На этот раз это связано с тем, что рендеринг компонента функции, то есть выполнение, каждый раз, когда он выполняется повторно, все в области действия функции начинается заново. Это эквивалентно последнему рендерингу компонентаconst handleClick = () => {}, а затем снова визуализироватьconst handleClick = () => {}, это не одно и то же

export default () => {
  const handleClick = () => {} 
  return (
    <div>
      <IfEqual onClick={handleClick} />
    </div>
  )
}

В этом случае мы можем использовать второй параметр memo, чтобы сохранить ситуацию более чем одного рендеринга:

// props: { a: 1, onClick: () => {} }
// 在我们知道onClick是做同一个事情的函数的前提下,不比较onClick
React.memo(C, (nextProps, prevProps) => nextProps.a === prevProps.a)

Наконец, передние и задние стойкиonClick, они одинаковы только в одном случае - вытаскивание объявления из компонента

const handleClick = () => {} 
export default () => {
  return (
    <div>
      <IfEqual onClick={handleClick} />
    </div>
  )
}

Вспомнили ли вы, что компонент класса всегдаonClick={this.handleClick}Шерстяная ткань?this.handleClickЭто всегда одна и та же функция. В этом случае, когда подкомпонент является функциональным компонентом, обертывание слоя памятки может достичь эффекта чистого компонента.

useCallback

Функциональный компонент может решить проблему, написав определение функции снаружи. Однако, если handleClick зависит от каких-то переменных внутри компонента, то handleClick должен быть прописан в нем (конечно, это решается с помощью ссылочных типов). Или пишите как обычно, полагайтесь на второй параметр memo, чтобы контролировать, следует ли повторно отображать компонент подфункции. Но не смотря ни на что есть проблема, то есть какой кусок кода в нем написан, выполнения кода и переопределения функций не избежать, типа

function a(){
    const b = function(){
        console.log(1)
        // 很多很多代码
    }
}
a()
a() // 函数b又被定义了一次

Если мы определяем, является ли это одной и той же функцией дважды до и после через зависимости, мы можем использовать функциональную память для реализации всей функции.

// 半伪代码
let prev
let prevDeps
function memorize(fn, deps) {
    // 前后依赖一致,不用重新计算直接返回上次结果
    if (prev && isEqual(deps, prevDeps)) {
        return prev
    }
    prevDeps = deps
    prev = fn
    return fn
}

function a(){
    const b = memorize(function(){
        console.log(1)
        // 很多很多代码
    }, [])
}
a()
a() // 函数b是同一个

Сходный с принципом функции памяти, а позжеuseCallbackПри появлении нового решения функция генерируется на основе зависимостей:

const handleClick = useCallback(() => {
    console.log(dep)
}, [dep])

Когда dep остается неизменным, handleClick является одной и той же функцией каждый раз, когда выполняется функциональный компонент. Если dep изменится, handleClick снова станет новой функцией.

export default () => {
// 没有依赖,永远是同一个函数
const handleClick = useCallback(() => {}, []);

// 依赖a,重新执行函数组件,a不变的,是同一个函数
// a变了handleClick是新的函数
const handleClick1 = useCallback(() => {}, [a]);
  return (
    <div>
      <IfEqual onClick={handleClick} />
    </div>
  )
}

Компонент реакции также является функцией, то есть фактическиuseCallbackВы также можете сделать функциональный компонент:

export default () => {
const handleClick = useCallback(() => {}, []);
const Cpn = useCallback(({ name }) => {
    return <button onClick={handleClick}>{name}</button>
}, [handleClick]);

  return (
    <div>
      <Cpn name="hi" />
    </div>
  )
}

Конечно, это всего лишь простой сценарий. Если вы используете хуки, вы не решили проблему или не придумали элегантную технику инкапсуляции. Если вы хотите использовать высокоуровневые компоненты, вы можете попробовать это. вне.useCallback

useMemo

const a = useMemo(() => memorizeValue, deps)

Когда deps остается неизменным, значение a остается последним memorizeValue, что сохраняет процесс пересчета. Если memorizeValue является функцией, она имеет тот же эффект, что и useCallback:

useCallback(fn, inputs) <=> useMemo(() => fn, inputs)

Мы можем попробовать код, выполняемый синхронно, когда времени очень много, его роль может сыграть useMemo:

// 强行更新组件
const useForceUpdate = () => {
  const forceUpdate = useState(0)[1];
  return () => forceUpdate(x => x + 1);
}
// 一个很耗时间的代码
function slowlyAdd(n) {
  console.time('add slowly')
  let res = n;
  for (let i = 0; i < 2000000000; i++) {
    res += 1;
  }
  console.timeEnd('add slowly')
  return res;
}

// useMemo记忆结果的一个自定义hook
function useSlowlyAdd(n) {
  const res = useMemo(() => {
    return slowlyAdd(n);
  }, [n])
  return res;
}

export default () => {
  const [count, add] = useState(1);
  const forceUpdate = useForceUpdate();
  const handleClick = useCallback(() => {}, []);
  useSlowlyAdd(count) // 第一次这里会耗很多时间,页面卡死一阵
  return (
    <>
      <button onClick={forceUpdate}>更新页面</button>
      <button onClick={() => add(count + 1)}>+</button>
    </>
  )
}

В первый раз, когда я зашел, страница некоторое время не отвечала, потому что slowAdd взял на себя основной поток js. Когда мы нажимаем «Обновить страницу» для обновления, страница не застревает, и компонент повторно отображается один раз. Когда мы нажали +, страница на некоторое время начала зависать.

Это связано с тем, что когда вы нажимаете +, зависимость n для useMemo изменяется, а n изменяется и пересчитывается, а вычисление занимает время. Если вы нажмете обновить страницу и она не будет изменена, чтобы зависеть от n, она не будет пересчитана, и страница не будет зависать.

Конечно, useMemo можно использовать и как компонент более высокого порядка, при этом его можно записать в виде reactElement:

const HOC = useMemo(() => <C />, deps)

наконец

Существуют следующие компоненты: Big — это компонент с узлами 10w, каждый узел привязан к событию.

const handleClick = useCallback(() => {}, []);
export default () => {
  return (
    <div>
      <Big onClick={handleClick} />
    </div>
  )
}

Если Большой компонент не обёрнут мемо, то производительность первого монтирования и перерендеринга родительского компонента следующая:

Если Большой компонент завернут в мемо и пропсы считаются одинаковыми, производительность первого монтирования и повторного рендеринга родительского компонента будет следующей:

ноОптимизация производительности — это не бесплатный обед, не все функциональные компоненты включают memo, а функции в компоненте включают usecallback. Потому что он запоминает, вынужден оптимизировать без смысла оптимизации, но производительность ухудшается.

Подводя итог, когда значение свойства props является функцией, как сделать так, чтобы дочерний компонент не выполнял повторно избыточный рендеринг: