Material-ui: Улучшение производительности Material-UI

Созданный на 23 мар. 2018  ·  93Комментарии  ·  Источник: mui-org/material-ui

Во-первых, большое спасибо за эту замечательную библиотеку компонентов! Это великолепно!

Я добавил ящик в свое новое приложение. В основном я скопировал пример ящика. Только ради PoC я приумножил

        <Divider />
        <List>{mailFolderListItems}</List>

раздел.

После этого он кажется очень медленным, особенно на мобильном устройстве (нексус 4, кордова с пешеходным переходом 20). Я использую режим разработчика, но режим продакшена не сильно ускоряется.

С помощью инструментов разработки реакции я заметил, что компоненты в mailFolderListItems отображаются при каждом щелчке по ссылке в response-router или даже при открытии меню. Иногда требуется до 50-60 мсек, чтобы повторно отрендерить ОДИН {mailFolderListItems} . я использую

const modalProps = {
    keepMounted: true, // Better open performance on mobile.
};

Чтобы устранить неопределенность с другими компонентами приложения, я преобразовал mailFolderListItems в Component и отключил повторную визуализацию:

class MailFolderListItems extends React.Component<{}, {}> {

    shouldComponentUpdate() {
        return false;
    }

    render() {
        return (
            <List>
                <Link to={Routes.Startpage.path}>
                    <ListItem>
                        <ListItemIcon>
                            <InboxIcon />
                        </ListItemIcon>
[...]


                <Divider />
                <MailFolderListItems />

После этого все в порядке.

Я нашел https://github.com/mui-org/material-ui/issues/5628 . Предлагаю вернуться к нему . Оптимизация shouldComponentUpdate - фундаментальный и самый важный шаг к повышению производительности. PureComponent - это самый распространенный ярлык.

Более того, я заметил, что очень много времени (1-2 мс и более для КАЖДОГО компонента material-ui) тратится на WithStyles .

  • [x] Я искал проблемы в этом репозитории и считаю, что это не дубликат.

Ожидаемое поведение

Я ожидаю получить от этой замечательной библиотеки максимальную производительность реакции.

Текущее поведение

Приложение становится медленнее с каждым компонентом пользовательского интерфейса материала.

Шаги по воспроизведению (для ошибок)

Я пока не привожу воспроизводящий пример, потому что я просто скопировал с демо-страницы компонента, но при необходимости могу предоставить демо-версию codeandbox. Для браузера это заметно, если скорость браузера в настройках производительности будет> = 5x.

Ваше окружение

| Технология | Версия |
| -------------- | --------- |
| Материал-UI | 1.0.0-beta.38 |
| Иконки Material-UI | 1.0.0-beta.36 |
| Реагировать | 16.2.0 |
| браузер | пешеходный переход кордовы 20 (равно android chrome 50) |

discussion performance

Самый полезный комментарий

@oliviertassinari, как отличный разработчик, как можно писать такие вещи? Почему вы используете свои личные предположения в качестве аргументов, а не фактов? Думаю, выше я привел достаточно фактов. Я сделал все, чтобы показать это, потому что мне нравится этот проект и я хочу внести свой вклад. Этого не достаточно? Хорошо, тогда еще факты и _no_ предположения.

Только для вас уменьшаю до 10 кнопок. 10! Это означает, что любые 10 компонентов (хуже: контейнеров) из material-ui замедляют работу всего приложения до тех пор, пока оно не станет непригодным для использования! Протестировано на реальном устройстве с пешеходным переходом 21 / хром 51 без дроссельной заслонки:

Обычная кнопка:
image

PureButton:
image

Это все еще 8-кратное улучшение! Это огромная! Представляете, насколько это важно на мобильном устройстве?

Вместо 100 кнопок вы найдете не более 10 кнопок на странице. Тем не менее, вы найдете 10 сеток, 10 X и т. Д.

Я использовал кнопку, потому что это один из самых простых компонентов! Это показывает, что material-ui не работает с точки зрения производительности. Теперь представьте себе такие компоненты контейнера, как AppBar, Toolbar, List, Drawer! Это еще хуже! Вы очень быстро получаете до 20 компонентов / контейнеров на каждой странице. И поскольку у вас не заканчивается медлительность на вашем мощном настольном компьютере / Mac, это не значит, что material-ui не невероятно медленный.

response-intl, проверка между предыдущим и новым реквизитом всегда будет ложной. Вы потратите впустую циклы процессора. Итак, x13 -> x0.8

Покажи мне пример на codeandbox. Я не понимаю, почему это должно происходить. Я уверен, что это происходит только при неправильном использовании компонентов реакции. И официальный пример показывает неправильное использование, потому что кажется, что response-intl не применяет подписчиков контекста . Но есть много других компонентов, которые согласованы с руководящими принципами реагирования и передовыми практиками, чтобы оставаться эффективными.

Кстати: WithStyles потребляет до 2,27 мс для кнопки на мобильном устройстве. 8 компонентов и вы под 60fps.

Все 93 Комментарий

Я ожидаю получить от этой замечательной библиотеки максимальную производительность реакции.

@Bessonov Performance будет в центре внимания релиза v1. Мы постарались сохранить ядро ​​библиотеки как можно быстрее. Этого должно хватить на + 90% случаев. По крайней мере, это мой опыт использования библиотеки до сих пор. Вы не предоставили никаких внешних ссылок, кроме привлечения нашего внимания к тому факту, что производительность важна, мы не можем решить эту проблему. Если вы можете определить коренное ограничение производительности Material-UI, которое мы можем изучить (с помощью кода воспроизведения и репозитория), поднимите его. А пока закрываю вопрос.

@oliviertassinari, спасибо за быстрый ответ! Я очень рад слышать, что после релиза этот спектакль будет в центре внимания.

Этого должно хватить на + 90% случаев. По крайней мере, это мой опыт использования библиотеки до сих пор.

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

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

Я дал ссылку на уже поднятую проблему и ссылку на документацию по реагированию.

Если вы можете определить корневое ограничение производительности Material-UI, которое мы можем исследовать (с помощью кода воспроизведения и ящика или репозитория)

Как я уже сказал, я могу это сделать. Вот сравнение чистого и нечистого компонента. Шаги по воспроизведению:

  1. Используйте Chrome и перейдите на https://codesandbox.io/s/r1ov818nwm
  2. В окне предварительного просмотра нажмите «Открыть в новом окне».
  3. Откройте инструменты разработчика и перейдите на вкладку «производительность».
  4. Уменьшите скорость вашего процессора как минимум в 5 раз (в зависимости от вашего ПК - может быть в 10 раз), чтобы соответствовать мобильному устройству.
  5. Нажмите на бургер и посмотрите, как он отстает.

И сейчас:

  1. Перейти к index.js
  2. изменить pure на true
  3. Сохранить
  4. Повторите шаги сверху
  5. Наслаждайся этим!

Этот пример немного нереалистичен, потому что я вставил слишком много элементов List. Но, как я уже сказал, на мобильных устройствах очень быстро появляется точка, в которой вы «чувствуете» каждое действие.

Вот моя проблема с WithStyles . Это на рабочем столе с дросселированием процессора. Хочу в понедельник выложить скриншоты с реального устройства.

Время для WithStyles(ListItem) :
image

Время для ListItem :
image

Разница составляет 1,26 мс только для компонента ListItem . С 13 такими компонентами вы больше не можете рендерить со скоростью 60 кадров в секунду. Но я заметил на рабочем столе, что эта продолжительность не всегда присутствует.

Вот сравнение чистого и нечистого компонента

Кстати, я не говорю, что PureComponent - это решение всех проблем с производительностью. Я просто говорю, что он может быть одним из помощников в создании интерфейса материала для мобильных устройств.

Только для компонента ListItem разница составляет 1,26 мс.

@Bessonov Компонент WithStyles должен быть очень дешевым для повторяющихся экземпляров одного и того же компонента. Мы вводим CSS только один раз.
Возможно, вы достигли ограничений React и стоимости компонентов более высокого порядка.
Существует ряд решений для уменьшения проблемы с производительностью в React. Например, вы можете использовать мемоизацию и виртуализацию элементов. Я оставляю этот вопрос открытым как отправную точку для будущего исследования производительности.
Я не думаю, что мы можем что-то сделать прямо сейчас. Спасибо, что подняли вопрос.

Хорошо, это только одна часть. Что вы думаете о более важной части - оптимизации shouldComponentUpdate ?

РЕДАКТИРОВАТЬ: я хочу разделить эту проблему на две части. Эта часть касается shouldComponentUpdate а другая часть - WithStyles , если я могу показать дополнительную информацию. Это тебя устраивает?

оптимизация shouldComponentUpdate

@Bessonov Мы не реализуем такую ​​логику специально на стороне Material-UI по двум причинам:

  1. Большинство наших компонентов принимают реагирующие элементы, ссылки на реагирующие элементы меняются при каждом рендеринге, из-за чего логика систематически тратит циклы ЦП.
  2. Чем ближе логика shouldComponentUpdate находится от корня дерева React, тем она эффективнее. Я имею в виду, что чем больше вы сможете обрезать дерево, тем лучше. Компоненты Material-UI находятся на низком уровне. Возможности с низким кредитным плечом .

Единственная возможность, которую мы нашли, - это иконки. Сообщите нам, если сможете найти новых.

другая часть для WithStyles

+ 90% времени, затрачиваемого на WithStyles, на самом деле тратится на JSS , и мы мало что можем с этим поделать на стороне Material-UI.
По крайней мере, в последнее время я обнаружил возможность кэширования для рендеринга на стороне сервера, чтобы значительно улучшить производительность при повторном рендеринге. Но это только на стороне сервера.

Большинство наших компонентов принимают реагирующие элементы, ссылки на реагирующие элементы меняются при каждом рендеринге, заставляя логику систематически тратить циклы ЦП.

Это зависит от использования и может быть улучшено. Я не тестирую себя, но уверен, что правильное использование компонента и оптимизированный shouldComponentUpdate (с неглубоким сравнением) дешевле, чем неоптимизированный компонент, особенно для неизменяемых структур (и immutable.js не требуется Кстати). Связанный билет: https://github.com/mui-org/material-ui/issues/4305

Например, в https://github.com/mui-org/material-ui/blob/v1-beta/docs/src/pages/demos/drawers/PermanentDrawer.js#L68 это приводит к новым объектам и приносит PureComponent бессмысленно:

        classes={{
          paper: classes.drawerPaper,
        }}

потому что он каждый раз возвращает новый объект. Но нет:

const drawerClasses = {
    paper: classes.drawerPaper,
};
[...]
        classes={drawerClasses}

То же самое и с компонентами.

Чем ближе логика shouldComponentUpdate к корню дерева React, тем она эффективнее. Я имею в виду, что чем больше вы сможете обрезать дерево, тем лучше.

Да, это правда.

Компоненты Material-UI находятся на низком уровне. Возможности с низким кредитным плечом.

Отчасти это правда. Как вы можете видеть в https://codesandbox.io/s/r1ov818nwm, я просто оборачиваю List в PureComponent . Ничего другого, и это значительно ускоряет работу Drawer. Я бы сказал, что это верно для всех компонентов, по крайней мере, которые используют {this.props.children} . Я создал еще одну демонстрацию: https://codesandbox.io/s/my7rmo2m4y

Есть только 101 кнопка (pure = false) и Buttons, завернутые в PureComponent (pure = true, см. Откуда импортируется кнопка). Тот же результат игры, если я нажму кнопку «Щелкнуть»:

Обычная кнопка:
image

Обернутая кнопка (с накладными расходами и т. Д.):
image

Как видите, у обычных кнопок 637 мс против 47 мс! Вы все еще думаете, что оптимизировать shouldComponentUpdate (или просто PureComponent ) не стоит? :)

РЕДАКТИРОВАТЬ: исправить формулировку в начале

@oliviertassinari @oreqizer Заметил интересную вещь. Похоже, что extends PureComponent работает лучше, чем Component с

  shouldComponentUpdate() {
    return false;
  }

image

РЕДАКТИРОВАТЬ: Я думаю, что это одна из внутренней оптимизации реакции.

Как видите, у обычных кнопок 637 мс против 47 мс! Вы все еще думаете, что оптимизировать shouldComponentUpdate (или просто PureComponent) не стоит? :)

@Bessonov Это демонстрирует потенциал чистой логики. Да, это может быть очень полезно! Это улучшение в 13 раз. Но я не думаю, что это близко к реальным условиям:

  • Вместо 100 кнопок вы найдете не более 10 кнопок на странице. Тем не менее, вы найдете 10 сеток, 10 X и т. Д.
  • Вместо простых свойств, предоставляемых элементам нижнего уровня, вы будете использовать что-то вроде response-intl, проверка между предыдущим и новым props всегда будет ложной. Вы потратите впустую циклы ЦП . Итак, x13 -> x0.8 или около того

@oliviertassinari, как отличный разработчик, как можно писать такие вещи? Почему вы используете свои личные предположения в качестве аргументов, а не фактов? Думаю, выше я привел достаточно фактов. Я сделал все, чтобы показать это, потому что мне нравится этот проект и я хочу внести свой вклад. Этого не достаточно? Хорошо, тогда еще факты и _no_ предположения.

Только для вас уменьшаю до 10 кнопок. 10! Это означает, что любые 10 компонентов (хуже: контейнеров) из material-ui замедляют работу всего приложения до тех пор, пока оно не станет непригодным для использования! Протестировано на реальном устройстве с пешеходным переходом 21 / хром 51 без дроссельной заслонки:

Обычная кнопка:
image

PureButton:
image

Это все еще 8-кратное улучшение! Это огромная! Представляете, насколько это важно на мобильном устройстве?

Вместо 100 кнопок вы найдете не более 10 кнопок на странице. Тем не менее, вы найдете 10 сеток, 10 X и т. Д.

Я использовал кнопку, потому что это один из самых простых компонентов! Это показывает, что material-ui не работает с точки зрения производительности. Теперь представьте себе такие компоненты контейнера, как AppBar, Toolbar, List, Drawer! Это еще хуже! Вы очень быстро получаете до 20 компонентов / контейнеров на каждой странице. И поскольку у вас не заканчивается медлительность на вашем мощном настольном компьютере / Mac, это не значит, что material-ui не невероятно медленный.

response-intl, проверка между предыдущим и новым реквизитом всегда будет ложной. Вы потратите впустую циклы процессора. Итак, x13 -> x0.8

Покажи мне пример на codeandbox. Я не понимаю, почему это должно происходить. Я уверен, что это происходит только при неправильном использовании компонентов реакции. И официальный пример показывает неправильное использование, потому что кажется, что response-intl не применяет подписчиков контекста . Но есть много других компонентов, которые согласованы с руководящими принципами реагирования и передовыми практиками, чтобы оставаться эффективными.

Кстати: WithStyles потребляет до 2,27 мс для кнопки на мобильном устройстве. 8 компонентов и вы под 60fps.

Почему вы используете свои личные предположения в качестве аргументов, а не фактов?

Что заставляет вас думать, что это личное предположение? Я пытался мыслить критически. С концептуальной точки зрения, дополнительный обход пропов замедлит чистую версию по сравнению с не чистой версией, он должен сократить что-то, что того стоит. Те же аргументы, что и # 5628 или https://github.com/react-bootstrap/react-bootstrap/issues/633#issuecomment -234749417 или https://github.com/reactstrap/reactstrap/pull/771#issuecomment -375765577

С чистым:
capture d ecran 2018-03-27 a 11 43 41

Без чистого:
capture d ecran 2018-03-27 a 11 44 15

Воспроизведение следующее.

@oliviertassinari , вы уверены, что codeandbox все исправит для вашего теста? Потому что мои результаты очень разные:

Без чистого (даже без дросселя медленно):
image

Чистый (после изменения на true и сохранения я получаю новый URL-адрес дляcodeandbox):
image

Поскольку он не проверяет изменения контекста, а также заставляет пользователей следить за тем, чтобы все дочерние компоненты также были «чистыми», я не считаю, что это хорошо подходит для этой библиотеки. Эта библиотека должна быть максимально гибкой, и я считаю, что это затруднит ее использование.

Я вижу суть. Но это очень интересный компромисс. С одной стороны, даже material-ui должен быть «максимально гибким», но с другой стороны, текущая производительность делает его вообще непригодным для использования.

Возможно, вам стоит подумать о предоставлении чистой и не чистой версии компонентов. Я делаю это сейчас, чтобы мое приложение работало даже на настольном компьютере.

@Bessonov Неправильно сохранен

<Button>
+ <i>
    Button
+ </i>
</Button>

Я не понимаю, почему это должно давать разные результаты? Я получаю лучшую диаграмму, но не чистая версия значительно медленнее.

РЕДАКТИРОВАТЬ: хорошо, понятно. Попробуй разобраться, что происходит ...

Хорошо, сейчас я понимаю. То же самое «новый объект на каждом рендере». Раньше я этого не замечал. В некоторых случаях это можно улучшить с помощью констант через плагин babel автоматически.

Что ж, тогда вы это уже знаете! : D Даже сама по себе не так много пользы (вы показали ~ 7%), но в итоге все равно выгодно использовать чистые компоненты, чтобы избежать некоторых недостатков, о которых вы упомянули выше. Я протестировал его сейчас с помощью Pure Wrapper + плагин babel + производственная сборка, и он впечатляет быстро на том же мобильном устройстве!

Как я уже сказал, я считаю, что лучше всего предлагать и не чистые компоненты для гибкости, и чистые компоненты (достаточно оберток, чтобы сделать его простым и поддерживаемым) для повышения производительности. Но что касается меня, я могу жить только с чистыми компонентами, потому что общее улучшение производительности намного больше, чем недостатки производительности. Или лучше: я не могу использовать material-ui без чистых компонентов.

Хорошо, пока я жду дальнейших комментариев по этой теме и создаю собственные оболочки в моем приложении)

Спасибо за идеи!

Я никогда не слышал о том, чтобы элементы transform-react-constant-elements действительно использовались и были действительно полезными. Это нормально, если добавить случайную микрооптимизацию, но на практике вы редко пишете код, достаточно простой, чтобы извлечь из него максимальную пользу. Хотя, я полагаю, это была бы неплохая оптимизация для всех компонентов значков в стиле SVG, таких как <Add /> .

Взгляните на этот пример (нажмите «Плагины» сбоку, найдите «response-constant» и установите флажок «transform-react-constant-elements»), и вы увидите, что почти ничего не было оптимизировано:

  • Простой статический InputAdornment перемещен наверх, ура.
  • Но тривиально простая кнопка отправки больше не была оптимизирована в тот момент, когда мы добавляли к ней обработчик событий.
  • И хотя сам InputAdornment теперь поднят, InputProps={{startAdornment: ...}} все еще встроен и создает новый объект при каждом рендеринге, что делает невозможным использование PureComponent.
  • Точно так же classes={{label: classes.runButtonLabel}} делает невозможным для PureComponent оптимизацию кнопки «Выполнить».

Мне лично нравится PureComponent, и я стараюсь использовать его абсолютно везде и оптимизировать вещи, насколько это возможно, чтобы он работал. Но не похоже, что MUI вообще сделан так, чтобы PureComponent работал.

  • Реквизиты *Props такие как InputProps являются фундаментальным шаблоном того, как работает MUI. Это не просто продвинутый способ изменения внутренних компонентов MUI, когда он вам нужен, а то, что регулярно используется в простых случаях использования. Этот шаблон часто делает любой листовой компонент, который обычно можно оптимизировать в чистом режиме, неоптимизируемым.
  • Точно так же шаблон classes={{...}} также не работает с PureComponent, и это способ добавления любого стиля к вещам в MUI. (И использование classes={classes} никоим образом не практично, потому что реальный потребитель, вероятно, имеет другое имя класса, чем внутренний класс компонента, и classes , вероятно, также будет включать классы, предназначенные для стилизации других элементов в том же потребляющем компоненте)
  • Дочерние элементы используются часто, вместо одного компонента в общем шаблоне часто используются 2-3 компонента, и только один из них может быть оптимизирован в чистом виде, если любой из них может.

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

Мы просто разрешаем включить чистый режим, потребители должны запоминать или поднимать объекты вручную, чтобы действительно получить оптимизацию.

  1. Один из способов, который я могу придумать для этого, - это немного не хватает shallowMemoize который использует локальные this и key (чтобы вы могли использовать его для разных битов данных) который запоминает данные до тех пор, пока они неглубоко равны
  2. Еще я могу придумать что-то вроде повторного выбора для создания селекторов, которые запомнят данные.
  3. Другой способ выполнить shallowMemoize в 1. - передать его render() с помощью декоратора. Таким образом, мы могли бы передавать новый каждый рендер, и вместо того, чтобы требовать key мы можем просто проверить, следует ли повторно использовать какие-либо запомненные объекты из последнего рендеринга, а затем отбросить все старые значения.

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

import {createSelector} from 'reselect';

class FormPage extends PureComponent {
  state = { example: '' };

  change = e => this.setState({example: e.target.value});
  submit = () => {
    console.log('Submit: ', this.state.example);
  };

  runButtonClasses = createSelector(
    props => props.classes.runButtonLabel,
    runButtonLabel => ({runButtonLabel}));

  render() {
    const {title} = this.props;
    const {example} = this.state;

    return (
      <form>
        {title}
        <TextField
          InputProps={this.shallowMemoize('a', {
            // This example assumes use of transform-react-constant-elements to make this object always the same
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}}
          onChange={example}
          value={example} />
        <Button classes={this.runButtonClasses(classes)}>Run</Button>
        <Button onClick={this.submit}>Submit</Button>
      </form>
    );
  }
}
// ...
  <strong i="6">@withShallowMemoize</strong>
  render(memo) {
    const {title} = this.props;
    const {example} = this.state;

    return (
      <form>
        {title}
        <TextField
          InputProps={memo({
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}}
          onChange={example}
          value={example} />
        <Button classes={memo(classes)}>Run</Button>
        <Button onClick={this.submit}>Submit</Button>
      </form>
    );
  }

Вместо того, чтобы пытаться оптимизировать InputProps, классы и т. Д., Мы призываем людей создавать микрокомпоненты для всех своих классов использования.

Если это рекомендуемый способ использования MUI, нам может даже не понадобиться чистый режим. Как видите, как только вы начнете создавать небольшие вспомогательные компоненты для общих сценариев использования, эти компоненты сами легко могут быть чистыми компонентами. В примере WeightTextField теперь никогда не будет повторно отрисован, пока value остается прежним, полностью пропуская withStyles, любую работу мемоизации, необходимую для InputProps, или настройку InputAdornment. И когда value действительно изменится, мы все равно должны повторно отрендерить TextField, поэтому не чистый InputProps={{...}} не имеет значения.

Меня устраивает этот путь. Теоретически мне нравятся микрокомпоненты; хотя я ненавижу каждый действующий в настоящее время синтаксис / шаблон для их написания, о котором я могу думать. Я не хочу MyComponent = enhance(MyComponent) , я хочу их украсить, но вы не можете украсить ни один из коротких способов написания крошечного компонента. Мне также не нравится превращать import {TextField} from 'material-ui'; в import WeightTextField from '../../../ui/WeightTextField ; `.

`` js
let WeightTextField = ({unit, InputProps, ... props}) => (
{...реквизит}
InputProps = {{
startAdornment:{Ед. изм} ,
... InputProps
}}
onChange = {пример}
значение = {пример} />
);
WeightTextField = чистый (WeightTextField);

RunButton = составить (
withStyles (theme => ({
метка: {
fontWeight: '800',
},
})),
чистый
)(Кнопка);

const SubmitButton = pure (Кнопка);

class FormPage extends Component {
состояние = {пример: ''};

change = e => this.setState ({пример: e.target.value});
submit = () => {
console.log ('Отправить:', this.state.example);
};

оказывать() {
const {название} = this.props;
const {пример} = this.state;

return (
  <form>
    {title}
    <WeightTextField
      unit='Kg'
      onChange={example}
      value={example} />
    <RunButton>Run</RunButton>
    <SubmitButton onClick={this.submit}>Submit</SubmitButton>
  </form>
);

}
}
`` ''

У меня есть вариант использования, когда мне нужно отобразить 500–2000 флажков на странице в большом списке. При использовании флажков в собственном браузере производительность нормальная, но при использовании компонента <Checkbox> производительность очень низкая и линейно масштабируется с количеством флажков на странице. Пример: https://codesandbox.io/s/5x596w5lwn

Я использую mui @ next - есть ли какая-то стратегия, которую я могу использовать _now_, чтобы сделать это работоспособным?

@wilsonjackson
Во-первых, не делайте следующего. Это создаст новый обработчик для каждого флажка при каждом рендеринге, что деоптимизирует любую оптимизацию PureComponent, которую вы пытаетесь выполнить.

  handleChange = index => event => {
    this.setState({

Во-вторых, создайте свой собственный небольшой компонент, чтобы обернуть Checkbox и сделать этот компонент чистым. Это дает дополнительное преимущество: вы можете добавлять любые свойства, общие для всех ваших флажков. И, поскольку у вас есть общая проблема, связанная с необходимостью отдельного обработчика событий изменения для каждого элемента, мы можем использовать компонент класса и сделать это в компоненте, а не в контейнере списка.

https://codesandbox.io/s/r7l64j6v5n

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

@dantman Эти варианты API были сделаны для того, чтобы максимально улучшить DX, пытаясь при этом быть достаточно быстрым.

Вместо того, чтобы пытаться оптимизировать InputProps, классы и т. Д., Мы призываем людей создавать микрокомпоненты для всех своих классов использования.

Да. Шаблон упаковки, безусловно, является рекомендуемым способом настройки библиотеки. Его можно расширить, чтобы применить оптимизацию производительности. Это проще в пользовательской среде, где вариативность использования компонентов намного ниже. Мы могли бы даже добавить в документацию FAQ или раздел руководства по этому поводу.

Да. Шаблон упаковки, безусловно, является рекомендуемым способом настройки библиотеки. Его можно расширить, чтобы применить оптимизацию производительности. Это проще в пользовательской среде, где вариативность использования компонентов намного ниже. Мы могли бы даже добавить в документацию FAQ или раздел руководства по этому поводу.

Ok. В этом случае:

  • Мы хотели бы порекомендовать библиотеки, такие как Recompose, которые упрощают написание небольших компонентов без сохранения состояния. Черт возьми, я был бы рад, если бы кто-то сказал мне, что есть другой шаблон или библиотека, которая похожа на перекомпоновку, но позволяет создавать чистые функции без сохранения состояния, такие как шаблоны перекомпоновки, но так же хорошо, как использование синтаксиса декоратора
  • Подобно тому, как мы перечисляем предложения о том, как использовать различные библиотеки автозаполнения и css в библиотеках js с MUI, мы, вероятно, также захотим предложить различные существующие шаблоны организации папок и библиотеки взлома пути (те, которые позволяют вам включать импорт ../../../../ui/Foo во что-то вроде something-local/Foo ), которое можно использовать для создания собственной локальной серии микрокомпонентов, обертывающих MUI, так же хорошо, как использование import {TextField} from 'material-ui'; .

@dantman фантастический, спасибо.

Мне приходилось несколько раз применять sCU из-за очень медленной работы withStyles (или, скорее, JSS). Я не знаю кода JSS, но мне кажется, что его можно довольно сильно оптимизировать. Я обычно использую стилизованные компоненты или гламурные и, таким образом, в конечном итоге использую JSS и один из других в приложении, и оба они превосходят JSS.

Хотя эти случаи могут немного раздражать, их легко обойти с помощью sCU на уровне приложения или более интеллектуальных обновлений состояния. Я еще не видел, чтобы один компонент MUI был достаточно медленным, чтобы вызвать проблемы, и мне еще предстоит кодировать внутри MUI, чтобы он занимал значительное время.

Нельзя сказать, что это не может быть быстрее и наверняка было бы лучше, если бы требовалось меньше контроля, но, по крайней мере, в том, что я вижу, в этом случае было бы лучше потратить время на оптимизацию JSS напрямую, чем MUI.

@Pajn Спасибо за отзыв. Было бы действительно здорово увидеть ситуацию, когда производительность withStyles проблематична или когда она уступает стилевым компонентам.

Кто-нибудь проверял это репо https://github.com/reactopt/reactopt ?

$ click - button (text: مقالات ) => CssBaseline,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,ScrollbarSize,TransitionGroup,TouchRipple,Ripple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,Main,ScrollbarSize,TransitionGroup,TouchRipple,Ripple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple

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

@ nimaa77

Кто-нибудь проверял это репо https://github.com/reactopt/reactopt ?
Нет, я использую простые возможности инструментов разработки « почему вы обновили» и chrome / react.

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

См. Обсуждение выше. Это не подходит для каждого случая использования. Мне кажется, что material-ui должен предлагать как чистые, так и не чистые версии компонентов. Я вижу огромное улучшение производительности с чистыми компонентами.

@dantman

Взгляните на этот пример (нажмите «Плагины» сбоку, найдите «response-constant» и установите флажок «transform-react-constant-elements»), и вы увидите, что почти ничего не было оптимизировано:

Это всего лишь один из способов решить эту проблему. Есть также другие варианты, плагины и другие оптимизации, созданные вручную. Не поймите меня неправильно, но меня не интересуют теоретические дискуссии о том, какие оптимизации хороши или плохи. Меня интересует практическая оптимизация, которая РАБОТАЕТ. По крайней мере, в моей настройке. Все, что я написал выше, РАБОТАЕТ для меня и делает мое приложение по крайней мере пригодным для использования на мобильных устройствах с низким и средним уровнем дохода. Хотя я вынужден делать больше оптимизаций, таких как переупорядочивание деревьев компонентов, достигнутая производительность была бы невозможна без чистых компонентов и других автоматических оптимизаций. И да, я очень много оптимизирую, чтобы добиться этого.

@Bessonov, возможно, мы можем использовать опору, чтобы сделать метод shouldComponentUpdate для поверхностного сравнения (https://reactjs.org/docs/shallow-compare.html) ИЛИ всегда возвращать false,
это не увеличит размер пакета с двумя версиями компонентов (чистая и нормальная)

@lucasljj Я не ожидаю значительного увеличения размера пакета, если это будет сделано как оболочка для компонента с отслеживанием состояния, как упомянуто выше. См. Также профили выше: только чистые компоненты быстрее, чем return false; в sCU.

Проблема с чистыми компонентами или компонентами, реализующими sCU, заключается в том, что вы используете внутри него не чистый компонент или children . См. Утверждения выше. Еще одна проблема, которую необходимо решить, - это переключение тем. Не тестировалось, но я думаю, что это можно преодолеть, по крайней мере, с помощью Context API.

@bossonov Чистые компоненты считаются обязательной

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

@oliviertassinari

Производительность будет в центре внимания релиза v1.

Отлично, v1 выпущена :) Есть идеи, когда надо поднять производительность? Я заметил, что эта проблема не относится к этапу публикации v1.

@Bessonov Я думаю, что было бы здорово потратить некоторое время на обновление ROADMAP. По поводу производительности. У меня есть две важные вещи, но я надеюсь узнать больше:

  1. Мы не можем улучшить то, чего не видим. Нам нужен эталон. Пока я видел две интересные библиотеки:
  2. Для рендеринга на стороне сервера CSS требуется ~ 30% того, что нужно для рендеринга React. Потому что мы не поддерживаем style = f(props) (ну, пока не поддерживаем: # 7633). Мы можем реализовать очень эффективное кэширование, сократив стоимость повторных запросов почти до 0%. Я мог бы поработать над этим для офиса, если производительность SSR вредит нашим бизнес-показателям.
    Но это не единственные варианты, мы также можем подумать о плагинах Babel для применения дорогостоящих пресетов JSS во время компиляции, а не во время выполнения (чтобы сбалансировать последствия размера пакета) cc @kof.

Спасибо за адреса.

Полностью согласен с 1 и связанным # 4305 выше.

  1. SSR может помочь с загрузкой первой страницы, но не помогает с медленной повторной отрисовкой или кордовой. Хотя лично я могу игнорировать первоначальную загрузку на настольном компьютере или даже на мобильном устройстве, медленный повторный рендеринг по-прежнему влияет на мобильные устройства после первой загрузки.

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

Другое дело - своего рода документация или пример проекта, как использовать mui (и babel и т. Д.) Для повышения производительности.

Не знаю, можно ли использовать jss-cache .

Конвейер предварительной обработки ISTF + сможет сэкономить несколько мс.

Сб, 19 мая 2018 г., 19:06 Антон Бессонов [email protected] написал:

Спасибо за адреса.

Полностью согласен с 1 и связан # 4305
https://github.com/mui-org/material-ui/issues/4305 выше.

  1. SSR может помочь с загрузкой первой страницы, но не поможет с медленной
    рендеринг или кордова. Хотя лично я могу игнорировать первую загрузку на
    на настольных компьютерах или даже на мобильных устройствах, медленное повторное отображение по-прежнему влияет на мобильные устройства после
    первая загрузка.

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

Другое дело - своего рода документация или пример проекта, как использовать mui.
(и babel и т. д.), чтобы получить лучшую производительность.

Я не знаю, может ли jss-cache http://cssinjs.org/jss-cache?v=v3.0.0 быть
использовал.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/mui-org/material-ui/issues/10778#issuecomment-390418709 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/AADOWGrCxNGqrT4MijiX8r9Ad32z6RsJks5t0FEtgaJpZM4S4woq
.

Может быть полезно для настройки тестов:
https://github.com/A-gambit/CSS-IN-JS-Benchmarks/blob/master/RESULT.md

О, response-jss (который используется material-ui) кажется довольно медленным

@janhoeck, это не так, и я уверен, что вы не сможете доказать обратное.

@kof Я прекрасно понимаю, что очень медленные. На самом деле, в целом у Material-ui не все так хорошо.

4x-6x по сравнению с афродитой очень медленный

@Bessonov Методология тестирования
https://twitter.com/necolas/status/954024318308007937?lang=fr

Результаты в моем браузере:
⚠️ с неуверенностью

capture d ecran 2018-06-12 a 17 58 54

общая производительность material-ui не блестит

Мы вполне можем быть ограничены самим React и стоимостью компонентов.

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

Что касается производительности MUI, вы можете стать быстрее, если вообще не используете никаких абстракций, но дело не в этом. MUI next работает довольно быстро. Он настолько быстр, что вам не стоит беспокоиться о его производительности, если только ВЫ не сделали что-то совершенно неправильно или неправильно использовали библиотеку.

@kof нет, MUI по умолчанию не работает быстро. Мы вынуждены применять обходные пути, чтобы добиться приемлемой производительности. Совершенно точно: разве вы не видели, что речь идет о мобильных устройствах, а не о Mac высокого класса?

никогда не беспокойтесь о его производительности, если только ВЫ не сделали что-то совершенно не так или неправильно использовали библиотеку

Ну, выше приведены некоторые примеры кодов. Если мы просто используем компоненты MUI в контейнере и сохраним входные значения в состоянии контейнера, как показано в демонстрациях компонентов, то он очень быстро станет непригодным для использования. Оберните PureComponent, используйте неконтролируемые компоненты, переупорядочивание дерева, константные элементы, мемоизируйте и так далее, помогая нам поддерживать приемлемую производительность. Приглашаем вас показать, как правильно использовать MUI для получения лучших результатов, например, в (atm мы больше не используем ящик):
https://codesandbox.io/s/r1ov818nwm

@oliviertassinari спасибо за ссылку. Вот результаты Chrome 66 на nexus 4. Как видите, результаты в 10 раз хуже. Думаю, на пешеходном переходе 21 / хром 51 он может быть немного медленнее:

screenshot_2018-06-12-18-20-19

Мы вполне можем быть ограничены самим React и стоимостью компонентов.

ИМХО не обязательно, как уже упоминалось другими. Каждый избегаемый повторный рендеринг - это большой выигрыш для производительности и заряда батареи. Не забывайте, производительность - третий пункт в вашей дорожной карте: +1:

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

@oliviertassinari - это evtl. возможно, что оболочка mui повторно отображает стили jss при обновлении? Возможна некоторая утечка, которая делает ненужную работу.

@kof

Смотрите так [...]

Я понимаю вашу точку зрения. Но компоненты реагирования и реагирования (чистые) сами по себе не являются узким местом и работают очень хорошо. Например, MUI не использует PureComponent (с описанными выше плюсами и минусами), но они безопаснее для нашей жизни. @oliviertassinari упомянул, что есть шанс повысить производительность за счет использования кешей или прекомпилятора.

Не поймите меня неправильно, вы классные ребята, и я действительно рад каждому выпуску MUI: clap: Но вам также нужно учитывать производительность, потому что не каждый пользователь посещает веб-сайты через высокопроизводительный рабочий стол .

если реакция не является узким местом производительности, и вы в этом уверены, JSS тоже не будет. Единственное, что я мог представить, нужно проверить, если происходит какая-то ненужная работа и css восстанавливается при обновлении.

Чистый компонент - это то, что ВЫ должны использовать в своем коде на уровне приложения, если вам нужна оптимизация. MUI не обязан этого делать, это общая библиотека и не следует делать никаких предположений. PureComponent не всегда хорош.

JSS рассматривал производительность с первого дня, и я провел бесконечные часы в микро-оптимизациях.

Это смешно. Я не могу предотвратить повторное рендеринг компонентов пользовательского интерфейса при каждом взаимодействии. Неважно, что я делаю, даже если что-нибудь простое:

class RowText extends Component {
  shouldComponentUpdate = (nextProps, nextState) => {
    return false;
  };

  render() {
    const { title, artist } = this.props;
    return <ListItemText primary={title} secondary={artist} />;
  }
}

Триггеры повторно отрисовывают базовые элементы типографики. Как это вообще возможно ?

Эй, это происходит только со списком и элементом списка. Почему ? Что я могу сделать?

@ danielo515 Трудно сказать без полного примера. Компоненты List также используют контекст, поэтому, если вы также измените это значение, это также вызовет повторную визуализацию.

Я понимаю, что @ eps1lon. Однако не могли бы вы объяснить, как может произойти изменение контекста? Я ничего не передаю компоненту списка, просто визуализирую внутри него потерянных детей.

@ danielo515 Практически каждый раз, когда List выполняет рендеринг. Новый контекстный API позволил использовать некоторые стратегии оптимизации, которые мы могли изучить, запомнив значение контекста.

В настоящее время API контекста реагирует на повторную визуализацию для каждого потребителя, если значение изменяет WRT на строгое равенство. Поскольку мы создаем новый объект при каждом вызове рендеринга List каждый потребитель также будет выполнять рендеринг.

Так что на данный момент простым повышением производительности было бы обернуть List чтобы не повторять рендеринг так часто. Я бы порекомендовал сначала провести тесты или отказаться от рендеринга раньше. Повторные вызовы рендеринга Typography не должны быть такими уж плохими.

Обертывание компонентов с помощью React.memo хорошо работает для всего, что не имеет детей. У меня есть форма с 3 ExpansionPanel и некоторыми 14 FormControl, и она отстает на рабочем столе.

Без решения этих проблем с производительностью я не смогу продолжать использовать material-ui: s

@prevostc без примера сложно сказать, что не так.

@prevostc, не видя воспроизведения кодов и ящика, ни мы, ни вы не можем знать, связано ли это вообще с этой проблемой

@prevostc Создавайте собственные компоненты, не требующие дочерних элементов, которые затем можно запоминать.

т.е. создайте свой собственный чистый / запомненный компонент MyExpansionPanel который принимает реквизиты данных / событий, но не дочерние элементы, и отвечает за рендеринг одной панели расширения. Затем используйте этот <MyExpansionPanel ... /> для рендеринга каждой из ваших панелей расширения. Тогда повторный рендеринг будет ограничен одной панелью расширения (или двумя при переходе между двумя).

@oliviertassinari @kof @dantman
Вот репродукция кода моей проблемы с производительностью: https://codesandbox.io/s/yvv2y2zxxx

Это форма с ~ 20 полями (не редкость), вы будете испытывать некоторую задержку при вводе данных пользователем. На более медленных устройствах эту форму просто нельзя использовать.

Проблема с производительностью возникает из-за массового повторного рендеринга при вводе пользователя, но упаковка компонентов MUI в чистый компонент (React.memo) ничего не дает, поскольку все здесь имеет дочерние и дочерние элементы, это приведет к повторному рендерингу AFAIK (источник: https: // reactjs.org/docs/react-api.html#reactpurecomponent)

Ниже приведены несколько снимков экрана моего ручного теста: один без какой-либо оптимизации, один со всем запомненным и один с использованием настраиваемого компонента ввода с локальным состоянием, чтобы избежать слишком частой установки состояния для всей формы.
В каждой конфигурации согласование пользовательского ввода занимает около 60 мс (намного больше, чем 16 мс, которые мне нужны для рендеринга со скоростью 60 кадров в секунду.

Обратите внимание: я бы с радостью узнал, что сделал что-то не так, потому что мне нравится MUI, и мне было бы грустно, если бы не было простого решения <3

@dantman Как можно написать ExpansionPanel которое не принимает никаких входных данных React.ReactNode (дочерние элементы или свойства)? Если вы имеете в виду, что я должен написать определенный компонент для каждой панели в моем приложении, это невозможно, к сожалению, у меня их слишком много.

screenshot 2018-12-20 at 22 04 08

screenshot 2018-12-20 at 21 56 57

screenshot 2018-12-20 at 22 05 00

@dantman Как можно написать ExpansionPanel которое не принимает никаких входных данных React.ReactNode (дочерние элементы или свойства)? Если вы имеете в виду, что я должен написать определенный компонент для каждой панели в моем приложении, это невозможно, к сожалению, у меня их слишком много.

Да, вместо того, чтобы делать один массивный компонент с глубоко вложенным деревом панелей, разделите части панели расширения на компоненты. Так оптимизировать массивные деревья невозможно.

Это не невозможно. Компоненты React очень легкие. Скопируйте и вставьте блок кода из массивного компонента в функцию, и все почти готово, после этого вам просто нужно подключить реквизиты. И пока вы React.memo эту функцию и избегаете передачи вещей, которые нарушают чистую оптимизацию свойств, тогда все будет довольно легко оптимизировать.

Помните, если вы создаете небольшой компонент, чтобы отделить кусок от огромного компонента. Это не обязательно должно быть что-то сложное с собственным файлом и собственной проверкой свойств. Это может быть простой функциональный компонент в том же файле, что и компонент, в котором он используется, без propTypes. т.е. вы можете создавать небольшие компоненты, которые доступны только компоненту в том же файле, что и они.

Да, Material UI немного медленнее, чем элементы dom низкого уровня. Но даже если MUI Input был в 10 раз медленнее, чем необработанный input и после 100 все стало слишком медленно. Тогда у вас все равно будет проблема даже без MUI, потому что даже если он в 10 раз быстрее необработанного input сделало бы сайт одинаково медленным, если бы у вас их было 1000. Вы не можете использовать отдельные монолитные компоненты в React, даже если вы не используете MUI. Вам нужно разбить приложение на части разумного размера, чтобы React определил границы, которые он может оптимизировать.

@prevostc Вот ваша демонстрация, оптимизированная за счет разделения панели расширения на крошечный компонент,

Замечу, что это не просто хороший шаблон оптимизации, это хороший шаблон кодирования. В реальном коде, где вы не создаете массив входных данных, а явно записываете их с определенными именами, метками и целями. Поиск границ и повторяющихся шаблонов не только оптимизирует вещи, но и сокращает количество шаблонов, делая код более СУХИМ и читабельным. т.е. вместо InputWrapper я бы, вероятно, разбил эту комбинацию FormControl + InputLabel + FormHelperText + Input на небольшой локальный компонент SimpleTextInput. Это не только оптимизирует его (что приведет к тому, что несвязанные входные данные не будут повторно отображены), но также будет означать, что код не должен повторять дополнительный шаблон.

https://codesandbox.io/s/0o7vw76wzp

screen shot 2018-12-20 at 2 51 31 pm

Прочитав это, я пришел к выводу, что для оптимизации mui вам нужно создавать более мелкие конкретные компоненты. Это то, что я уже понял и успешно попробовал. Однако я также понял, что нет способа оптимизировать компоненты списка, потому что api контекста изменяет все входные реквизиты.

С Уважением

Хорошо, вот обновленный стресс-тест https://codesandbox.io/s/wz7yy1kvqk

Я согласен с @dantman в этом общем вопросе https://github.com/mui-org/material-ui/issues/10778#issuecomment -449153635 НО я не ожидал такой проблемы с производительностью при таком небольшом количестве компонентов, но терпите мне, поскольку я нашел источник моей проблемы с производительностью.

JSS медленный? (предупреждение о спойлере: нет)

Ссылаясь на некоторые из предыдущих комментариев в этом потоке, я добавил флажок в стресс-тесте, чтобы удалить все вызовы withStyles и пришел к выводу, что JSS работает быстро и не является источником проблемы с перфомансом. (как @kof указал на это в https://github.com/mui-org/material-ui/issues/10778#issuecomment-396609276).

screenshot 2018-12-22 at 15 17 26

Исправление

Для моего конкретного случая использования я смог точно определить проблему, заключающуюся в том, что каждый ввод формы был повторно отрисован при обновлении формы, хотя фактически изменился только один ввод.
На скриншоте ниже я заключил FormControl и Input в мемоизированный компонент, избегая рендеринга, если значение не изменилось. @dantman на самом деле предложил мне создать определенный компонент для каждого ExpansionPanel но это гораздо менее общее решение. В качестве примечания, каждая панель все еще перерисовывается, и производительность далека от оптимальной, но на данный момент ее достаточно.

screenshot 2018-12-22 at 15 18 22

Так? Что дальше?

Я думаю, что нет способа избежать такого рода проблем с изменением кода material-ui без серьезного изменения текущего API, сильно зависящего от состава React.ReactNode .
Но, как упоминал @dantman в https://github.com/mui-org/material-ui/issues/10778#issuecomment -449153635, MUI немного медленнее, чем ожидалось. ИМХО, вообще не заниматься этим - ошибка.

Зная об этой проблеме, нам может потребоваться создать страницу документа, посвященную проблемам с производительностью и способам их решения. Даже если его страница будет в основном перенаправляться на официальный документ (https://reactjs.org/docs/optimizing-performance.html) и перечислять компоненты, которые могут вызвать проблемы с производительностью, это только начало.
Было бы лучше console.warn предупредить пользователя, когда возникает такая проблема, но я не могу найти способ обнаружить проблемы на уровне материала ui.

@prevostc это сообщение сделало мой день

Я не: s

Я не знаю достаточно внутреннего MUI, чтобы иметь какое-либо представление о том, как улучшить его исходную производительность (без изменения api) прямо сейчас. Я работаю над некоторыми идеями, но на сегодня ничего неопровержимого: у меня есть приложение, в котором радиогруппа перерисовывается, когда ее прямой родитель не является, и пока не может воспроизвести это локально.

Любое изменение API будет заключаться в том, чтобы рассмотреть возможность удаления любых реквизитов React.ReactNode из API (дочерние элементы и другие реквизиты, такие как значок и т. Д.), Но я не мог найти хороший способ сохранить такую ​​же настраиваемость.
Вот что я пробовал: https://codesandbox.io/s/jpw36jw65 (предупреждение: не закончено).

Также стоит отметить, что MUI особенно медленный в режиме разработки из-за особенно медленной реакции в режиме разработки. Я не знаю, есть ли способ исправить это.

Есть ли какой-либо прогресс в добавлении возможностей (например, предложенных @Bessonov ) для оптимизации проблем с производительностью, с которыми в настоящее время сталкивается Material-UI?
Когда мы начали использовать эту замечательную библиотеку для нашего проекта, я не знал, что такие проблемы с производительностью могут возникнуть, когда проект становится все больше и больше; кроме того, я не видел ни одного раздела в документации по Material-UI, который бы информировал меня о случаях, когда Material-UI мог замедлиться и навредить UX.
В этом выпуске сообщается о множестве проблем с производительностью - прямо или косвенно связанных с Material-UI. Думаю, было бы неплохо перечислить их в другом выпуске, чтобы каждый мог отслеживать прогресс. Если ты думаешь, что все в порядке, я могу открыть новый выпуск.

@ mkermani144 Нам еще предстоит увидеть отчет о производительности, напрямую связанный с тем, что Material-UI делает неправильно. До сих пор этот выпуск использовался в качестве справочного форума для людей, испытывающих трудности. Вы подтверждаете, что не было сообщено о каких-либо действиях? Я собираюсь заявить очевидное: у абстракции React есть цена . Каждый компонент, оборачивающий хост, будет добавлять вес вашему дереву рендеринга, он замедляет его. Хотя вы можете отображать более 100 элементов списка с помощью собственного хоста, это начинает быть проблемой, когда вы оборачиваете их с помощью компонента класса. Это не относится к Material-UI.

Стол

Режим разработки

Возьмем, к примеру, таблицу. Это компонент, который люди считают медленным. Мы задокументировали виртуализацию , она очень помогает.

В следующем тестовом примере мы визуализируем 100 элементов в режиме разработки . Мы можем рассматривать следующие случаи:

  1. Таблица Raw: https://codesandbox.io/s/v066y5q7z3 : 63 мс в
  2. Table Material-UI Master: https://codesandbox.io/s/m4kwmvj9ly : 250 мс при рендеринге
  3. Table Material-UI Next: https://codesandbox.io/s/2o35yny1jn : 262 мс в

Таким образом, накладные расходы на использование Material-UI в режиме разработки над элементом хоста составляют около x4 (разница меньше в производстве!) Просто потому, что мы создаем промежуточные компоненты. Вот почему виртуализация становится важной после рендеринга списка из ~ 100 элементов таблицы. Теперь мы можем немного углубиться в то, почему и что мы можем с этим поделать?

  1. Таблица Raw + функциональный компонент. Почему функциональная составляющая? Мы хотим абстрагироваться от используемых имен классов. Мы не хотим, чтобы пользователи компонентов повторяли их.
    https://codesandbox.io/s/1zl75mwlpj : 105 мс при рендеринге
  2. Таблица Raw + функциональная составляющая + forwardRef. Почему forwardRef? Мы хотим, чтобы компонент был «прозрачным», чтобы иметь возможность доступа к элементу хоста с помощью ссылки.
    https://codesandbox.io/s/32o2y0o9op : 120 мс в
  3. Таблица Raw + функциональный компонент + forwardRef + withStyles. Почему withStyles? Потому что мы хотим стилизовать наш компонент:
    https://codesandbox.io/s/j2n6pv768y : 200 мс в рендере
  4. Таблица Raw + функциональный компонент + forwardRef + makeStyles. makeStyles может быть быстрее, чем withStyles, давайте попробуем:
    https://codesandbox.io/s/yw52n07l3z : 130 мс при рендеринге.

Итак, у нас есть одно преимущество: перенести все компоненты из withStyles в makeStyles. Мы можем выиграть около + 30% производительности (262 / (262-70)) в режиме разработки .

Режим производства

Я выполнил те же тесты в производственном режиме:

  • № 1 30 мс в
  • № 3 106 мс в
  • № 4 40 мс в
  • № 5 41 мс в
  • № 6 80 мс в
  • № 7 57 мс в

Таким образом, миграция с withStyles на makeStyles теоретически увеличивает ускорение на 30% в производственном режиме.

Если ты думаешь, что все в порядке, я могу открыть новый выпуск.

@ mkermani144 Если у вас есть конкретный случай, когда Material-UI делает это неправильно, конечно.

Я прочитал обо всех комментариях под этой проблемой. Моя проблема не вписывается ни в одну из упомянутых ранее.

У меня есть компонент List содержащий некоторые ListItem s, один из которых выбран и выделен. Когда я нажимаю другой ListItem чтобы выбрать и выделить его, весь список (содержащий его дочерние элементы) снова перерисовывается.

Я знаю, что эта проблема может показаться такой же, как и в предыдущем комментарии , но это не так; по крайней мере, я так думаю.

Посмотрим на результаты профилировщика React:

image
Как видите, у меня есть компонент MyList на верхнем уровне изображения. Этот компонент является лишь оболочкой вокруг MUI List и просто делает его чистым, то есть:

class MyList extends React.PureComponent {
  render() {
    return (
      <List>
        {this.props.children}
      </List>
    );
  }
}

Я добавил эту оболочку, потому что @ eps1lon в одном из своих комментариев упомянул, что повторный рендеринг List вызывает обновление контекста, и это обновление контекста заставляет всех потребителей (включая ListItem s) также повторно отображать .

Я также попытался сделать все мои ListItem чистыми и снова профилировал приложение. Результаты были такими же, за исключением того, что мой пользовательский компонент (например, MyListItem ) не повторно отображал _itself_, а все дочерние элементы ниже __did__.

Я знаю, что проблема возникает из-за того, что контекст, который MUI использует для стилизации, повторно отображает _somehow_. Но я не знаю, почему произошло это повторное рендеринг и как мне этого избежать.

Или я что-то не так делаю?

Примечание. Я использую новое (альфа) решение для стилизации MUI, то есть @material-ui/styles . Не знаю, имеет ли это значение.

@ mkermani144 Удалите Material-UI с собственными элементами,

Да, я знаю, что элементы являются объектами, а объекты не строго равны в Javascript, поэтому sCU не работает.

Но я не понимаю, что вы имеете в виду, когда говорите, что React.createElement вызывается снова. Какой звонок на createElement ? Если вы имеете в виду вызовы внутри List , он вызывает createElement для своих дочерних элементов ( ListItem s), только если он повторно отрисовывается. Если он не будет повторно отрисован, createElement не будет вызываться и повторного рендеринга произойти не должно. Проблема в том, что сам List рендерится заново.

@ mkermani144 Если вы можете создать пример минимального воспроизведения, мы можем взглянуть на него.

Ваш MyList (и, следовательно, List ) перерисовывается из-за того, что компонент, который отрисовывает MyList (назовем его MyComponent ), перерисовывается. PureComponent на MyList не помогает, так как MyComponent были повторно отрисованы и создали новые дочерние элементы для MyList так что проверка MyList s завершилась ошибкой.

Ваш MyComponent вероятно, будет повторно отрисован, потому что именно там вы храните состояние выбранного элемента.

Я думаю, что реализация списка MUI должна измениться, чтобы не воссоздавать значение контекста списка при каждом рендеринге.
здесь: https://github.com/mui-org/material-ui/blob/fb4889f42613b05220c49f8fc361451066989328/packages/material-ui/src/List/List.js#L57

Поэтому вместо этого List должен выглядеть примерно так:

const List = React.forwardRef(function List(props, ref) {
  const {
    children,
    classes,
    className,
    component: Component,
    dense,
    disablePadding,
    subheader,
    ...other
  } = props;
  const context = React.useMemo(() => ({ dense }), [dense]);

  return (
    <Component
      className={clsx(
        classes.root,
        {
          [classes.dense]: dense && !disablePadding,
          [classes.padding]: !disablePadding,
          [classes.subheader]: subheader,
        },
        className,
      )}
      ref={ref}
      {...other}
    >
      <ListContext.Provider value={context}>
        {subheader}
        {children}
      </ListContext.Provider>
    </Component>
  );
});

Это упростило бы создание sCU версий ListItems

Ваш MyList (и, следовательно, список) повторно отрисовывается из-за того, что компонент, который отрисовывает MyList (позволяет называть его MyComponent), перерисовывается. PureComponent в MyList не помогает, так как MyComponent был повторно отрисован и создал новые дочерние элементы для MyList, поэтому проверка MyLists завершилась ошибкой.

@Pajn Нет, посмотрите мои результаты профилировщика React . MyList не выполнял повторную визуализацию (он серый), но List выполнял (синий). Я не настаиваю на PureComponent за MyList . Несмотря на то, что я реализую sCU для MyList чтобы он не отображался повторно, List __ выполняет повторную визуализацию__.

@oliviertassinari
Я создал минимальный пример репродукции:

import React, { Component } from 'react';

import StylesProvider from '@material-ui/styles/StylesProvider';
import ThemeProvider from '@material-ui/styles/ThemeProvider';

import { createMuiTheme } from '@material-ui/core';

import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';

const theme = createMuiTheme({});

const MyListItem = React.memo(ListItem, (prev, next) => prev.selected === next.selected);

class App extends Component {
  state = {
    selected: null,
  }
  render() {
    return (
      <StylesProvider>
        <ThemeProvider theme={theme}>
          <List>
            {[0, 1, 2, 3, 4].map(el => (
              <MyListItem
                button
                selected={el === this.state.selected}
                onClick={() => this.setState({ selected: el })}
              >
                {el}
              </MyListItem>
            ))}
          </List>
        </ThemeProvider>
      </StylesProvider>
    );
  }
}

export default App;

Результаты профилировщика React (после нажатия на 4-й пункт списка):

image

Как видите, он работает должным образом, т.е. дополнительных повторных рендеров не требуется (за исключением компонентов ButtonBase внутри ListItem s). Проблема в том, что это воспроизведение слишком минимально; есть много вещей, которые я пропускаю в нем.

Я знаю, что вы не можете сказать мне, что не так с моим кодом, вызывая лишние повторные отрисовки. Но я задаю вам вопрос: что может вызвать повторный рендеринг в компонентах WithStylesInner которые обертывают компоненты MUI?

@ mkermani144 Что вы думаете об этом исправлении?

--- a/packages/material-ui/src/List/List.js
+++ b/packages/material-ui/src/List/List.js
@@ -40,6 +40,13 @@ const List = React.forwardRef(function List(props, ref) {
     ...other
   } = props;

+  const context = React.useMemo(
+    () => ({
+      dense,
+    }),
+    [dense],
+  );
+
   return (
     <Component
       className={clsx(
@@ -54,7 +61,7 @@ const List = React.forwardRef(function List(props, ref) {
       ref={ref}
       {...other}
     >
-      <ListContext.Provider value={{ dense }}>
+      <ListContext.Provider value={context}>
         {subheader}
         {children}
       </ListContext.Provider>

Вы хотите отправить запрос на перенос? :) Мы используем ту же стратегию с компонентом Table. Работает отлично. Спасибо, что сообщили о проблеме!

@oliviertassinari Конечно. Это именно то, что ранее предлагал PR .

14934 объединено; компонент List , однако, может стать узким местом производительности, если он достаточно велик, независимо от того, насколько он оптимизирован. Разве мы не должны предоставить пример, показывающий использование react-window или react-virtualized с компонентом List , как тот, который есть в Table docs?

Разве мы не должны предоставить пример, показывающий использование response-window или react-virtualized с компонентом List, как тот, который у нас есть в документации к таблицам?

Было бы здорово: +1:

Fwiw Я создал приложение для чата, и оно должно отображать большой список контактов. Я столкнулся с той же проблемой
@ mkermani144 имел.

https://stackoverflow.com/questions/55969987/why-do-the-children-nodes-rerender-when-the-parent-node-is-not-even-being-update/55971559

@ henrylearn2rock Думали ли вы об использовании виртуализации? Мы добавили демо для списка: https://next.material-ui.com/demos/lists/#virtualized -list.

Это тоже меня сбило с толку. Я думаю, что большинство людей (включая меня) полагали, что все в чистом компоненте безопасно от повторного рендеринга, что, очевидно, не относится к этой библиотеке. Я собираюсь попробовать виртуализацию, как вы недавно предложили. Спасибо!

Я думаю, что большинство людей (включая меня) полагали, что все в чистом компоненте безопасно от повторного рендеринга, что, очевидно, не относится к этой библиотеке.

Не так работают React.PureComponent или React.memo. Это влияет только на сам компонент. При изменении контекста дочерним элементам все равно может потребоваться повторная визуализация.

@pytyl Можете ли вы поделиться кодом, в котором вы использовали PureComponent и ожидали, что он предотвратит повторный рендеринг в его поддереве?

@ eps1lon в следующей документации
https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate-в действии

Поскольку shouldComponentUpdate вернул false для поддерева с корнем C2, React не пытался отрендерить C2, и поэтому даже не нужно было вызывать shouldComponentUpdate на C4 и C5.

Может я в этом ошибаюсь? Ниже приведен снимок моего профилировщика. Просто ради тестирования я явно возвращаю false для shouldComponentUpdate в моем компоненте Menu:

Screen Shot 2019-05-08 at 7 46 32 PM

Из-за этого все мои дочерние компоненты (Categories, Category, CategoryItems, CategoryItem) не перерисовывались. Многие вещи, связанные с MUI, по-видимому, перерисовываются внизу, что, кажется, вызывает большую задержку. Такие вещи, как withStyles, Typography, ButtonBase. Все еще немного новичок в React, поэтому, пожалуйста, извините за мое незнание. Ниже мой код для компонента меню (где я возвращаю false для shouldComponentUpdate):

import React, { Component } from "react";
import Categories from "./Categories";
import { withStyles, Paper } from "@material-ui/core";

const styles = theme => ({
  root: {
    paddingTop: 0,
    marginLeft: theme.spacing.unit * 2,
    marginRight: theme.spacing.unit * 2,
    marginTop: theme.spacing.unit * 1
  }
});

class Menu extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.categories.length == this.props.categories.length) {
      return false;
    }
    return true;
  }

  render() {
    const { classes, categories } = this.props;

    return (
      <Paper className={classes.root}>
        <Categories categories={categories} />
      </Paper>
    );
  }
}

export default withStyles(styles)(Menu);

Чтобы разобраться в проблеме, мне понадобится полный код и ящик.

@ eps1lon Я постараюсь сделать его на завтра. Спасибо.

@ eps1lon вот код:
https://codesandbox.io/s/348kwwymj5

Краткое описание
Это приложение с базовым меню для ресторанов (в которых часто более 100 пунктов меню). Когда пользователь щелкает элемент меню, открывается диалоговое окно «Добавить в заказ». Я приложу несколько ситуаций, когда профилировщик показывает низкую производительность (эти статистические данные отсутствуют в производственной сборке).

Система
MacBook Pro (Retina, 13 дюймов, начало 2015 г.)
Intel Core i7 с тактовой частотой 3,1 ГГц
Firefox 66.0.3

Случай 1 (пользователь нажимает на пункт меню)
Длительность рендеринга: 218 мс

Screen Shot 2019-05-10 at 4 45 26 AM

Screen Shot 2019-05-10 at 4 45 48 AM

Случай 2 (пользователь нажимает кнопку «Добавить в заказ» в диалоговом окне)
Длительность рендеринга: 356 мс

Screen Shot 2019-05-10 at 4 46 24 AM

Screen Shot 2019-05-10 at 4 47 10 AM

Я уверен, что совершаю здесь некоторую ошибку новичка, поэтому приветствуются любые рекомендации.

Поскольку WithStyles (ButtonBase) перерисовывается, я предполагаю, что WithStyles использует контекст, который воссоздается, хотя в этом нет необходимости.

Мне удалось найти это https://github.com/mui-org/material-ui/blob/048c9ced0258f38aa38d95d9f1cfa4c7b993a6a5/packages/material-ui-styles/src/StylesProvider/StylesProvider.js# где не могу найти

Знает ли @ eps1lon , может ли это быть причиной? Если это так, использование useMemo в объекте контекста, вероятно, исправит это. Хотя я не знаю, стабильны ли localOptions или нужно еще больше распространять useMemo.

Может быть. Но сначала вы должны выяснить, почему ваш компонент, использующий StylesProvider, выполняет рендеринг. Это либо что-то в верхней части вашего дерева, либо должно быть на некоторой границе пользовательского интерфейса. В любом случае их следует редко перерисовывать. Помните, что контекст реакции в любом случае не оптимизирован для частых обновлений.

Мы не должны преждевременно оптимизировать эти вещи только потому, что какой-то объект воссоздается во время рендеринга. Мемоизация - это не серебряная пуля. Так что без конкретного примера я ничего не могу сделать. Да, вещи переделывают. Иногда чаще, чем нужно. Но потраченный впустую повторный рендеринг не означает, что это причина снижения производительности.

@pytyl Я просмотрел ваш codeandbox, есть проблема с архитектурой рендеринга. Вы повторно визуализируете все, когда щелкаете по пункту меню. Ваш GlobalContext переходит на чистую логику.

@ eps1lon Думаю, нам следует закрыть этот вопрос. Лучше сосредоточиться на конкретно обозначенных проблемах.

TL; DR: создание срезов контекста, запоминание значений контекста, никаких проблем с material-ui в частности: https://codesandbox.io/s/8lx6vk2978

Я немного покопался, и проблема в том, что у вас есть этот большой глобальный контекст, который воссоздается во время рендеринга. Вы повторно визуализируете свое приложение, когда вы щелкаете, в этот момент глобальный контекст создается повторно. Ваш CategoryItem прослушивает его, который 100 раз появляется в вашем приложении. Поскольку у вас есть 100 элементов меню с материальным интерфейсом, вы встречаетесь с классической смертью от тысячи порезов.

По иронии судьбы частью решения является запоминание значения контекста, но важная часть - это идентификация отдельных срезов контекста. Кажется, что контекст состояния и отправки уместен. Это рекомендуется при использовании useContext с useReducer и, похоже, здесь подходит.

Это может создать довольно большое дерево и чертовски рендерить реквизиты, чем больше у вас будет контекстов. Я рекомендую вам взглянуть на useContext . Это очень поможет, если вы начнете сталкиваться с этими проблемами.

@oliviertassinari Собирать общие ошибки с решениями - это хороший вопрос. Мы можем решить, хотим ли мы создать из него отдельные выпуски.

@oliviertassinari @ eps1lon благодарим за просмотр! Производительность кажется отличной.

У меня просто была проблема с медленной производительностью рендеринга. Я решил это полностью, заменив все экземпляры компонента <Box> на <div> s. Я отлаживал с помощью Flamegraph React DevTools, и я перешел от 420 мс до 20 мс.

С <Box> es;
Screen Shot 2019-08-16 at 12 47 25 AM

Без <Box> es:
Screen Shot 2019-08-16 at 12 42 38 AM

@mankittens Вы можете сохранить компонент Box, используя стилизованные компоненты в качестве движка стилей. Производительность была бы намного лучше. В ближайшем будущем он должен улучшиться с JSS https://github.com/mui-org/material-ui/pull/16858.

Закрываю этот вопрос. Нам нужен специальный отчет о производительности для каждой потенциальной области улучшения, а не общая тема.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги