Во-первых, большое спасибо за эту замечательную библиотеку компонентов! Это великолепно!
Я добавил ящик в свое новое приложение. В основном я скопировал пример ящика. Только ради 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
.
Я ожидаю получить от этой замечательной библиотеки максимальную производительность реакции.
Приложение становится медленнее с каждым компонентом пользовательского интерфейса материала.
Я пока не привожу воспроизводящий пример, потому что я просто скопировал с демо-страницы компонента, но при необходимости могу предоставить демо-версию codeandbox. Для браузера это заметно, если скорость браузера в настройках производительности будет> = 5x.
| Технология | Версия |
| -------------- | --------- |
| Материал-UI | 1.0.0-beta.38 |
| Иконки Material-UI | 1.0.0-beta.36 |
| Реагировать | 16.2.0 |
| браузер | пешеходный переход кордовы 20 (равно android chrome 50) |
Я ожидаю получить от этой замечательной библиотеки максимальную производительность реакции.
@Bessonov Performance будет в центре внимания релиза v1. Мы постарались сохранить ядро библиотеки как можно быстрее. Этого должно хватить на + 90% случаев. По крайней мере, это мой опыт использования библиотеки до сих пор. Вы не предоставили никаких внешних ссылок, кроме привлечения нашего внимания к тому факту, что производительность важна, мы не можем решить эту проблему. Если вы можете определить коренное ограничение производительности Material-UI, которое мы можем изучить (с помощью кода воспроизведения и репозитория), поднимите его. А пока закрываю вопрос.
@oliviertassinari, спасибо за быстрый ответ! Я очень рад слышать, что после релиза этот спектакль будет в центре внимания.
Этого должно хватить на + 90% случаев. По крайней мере, это мой опыт использования библиотеки до сих пор.
На рабочем столе - да, это так. Но на мобильном телефоне это действительно медленно. Просто Drawer и некоторые кнопки заставляют приложение тормозить. Он не реагирует и потребляет больше энергии по мере необходимости.
Вы не предоставили никаких внешних ссылок, кроме привлечения нашего внимания к тому факту, что производительность важна, мы не можем решить эту проблему.
Я дал ссылку на уже поднятую проблему и ссылку на документацию по реагированию.
Если вы можете определить корневое ограничение производительности Material-UI, которое мы можем исследовать (с помощью кода воспроизведения и ящика или репозитория)
Как я уже сказал, я могу это сделать. Вот сравнение чистого и нечистого компонента. Шаги по воспроизведению:
И сейчас:
index.js
pure
на true
Этот пример немного нереалистичен, потому что я вставил слишком много элементов List. Но, как я уже сказал, на мобильных устройствах очень быстро появляется точка, в которой вы «чувствуете» каждое действие.
Вот моя проблема с WithStyles
. Это на рабочем столе с дросселированием процессора. Хочу в понедельник выложить скриншоты с реального устройства.
Время для WithStyles(ListItem)
:
Время для ListItem
:
Разница составляет 1,26 мс только для компонента ListItem
. С 13 такими компонентами вы больше не можете рендерить со скоростью 60 кадров в секунду. Но я заметил на рабочем столе, что эта продолжительность не всегда присутствует.
Вот сравнение чистого и нечистого компонента
Кстати, я не говорю, что PureComponent - это решение всех проблем с производительностью. Я просто говорю, что он может быть одним из помощников в создании интерфейса материала для мобильных устройств.
Только для компонента ListItem разница составляет 1,26 мс.
@Bessonov Компонент WithStyles должен быть очень дешевым для повторяющихся экземпляров одного и того же компонента. Мы вводим CSS только один раз.
Возможно, вы достигли ограничений React и стоимости компонентов более высокого порядка.
Существует ряд решений для уменьшения проблемы с производительностью в React. Например, вы можете использовать мемоизацию и виртуализацию элементов. Я оставляю этот вопрос открытым как отправную точку для будущего исследования производительности.
Я не думаю, что мы можем что-то сделать прямо сейчас. Спасибо, что подняли вопрос.
Хорошо, это только одна часть. Что вы думаете о более важной части - оптимизации shouldComponentUpdate
?
РЕДАКТИРОВАТЬ: я хочу разделить эту проблему на две части. Эта часть касается shouldComponentUpdate
а другая часть - WithStyles
, если я могу показать дополнительную информацию. Это тебя устраивает?
оптимизация shouldComponentUpdate
@Bessonov Мы не реализуем такую логику специально на стороне Material-UI по двум причинам:
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, см. Откуда импортируется кнопка). Тот же результат игры, если я нажму кнопку «Щелкнуть»:
Обычная кнопка:
Обернутая кнопка (с накладными расходами и т. Д.):
Как видите, у обычных кнопок 637 мс против 47 мс! Вы все еще думаете, что оптимизировать shouldComponentUpdate
(или просто PureComponent
) не стоит? :)
РЕДАКТИРОВАТЬ: исправить формулировку в начале
@oliviertassinari @oreqizer Заметил интересную вещь. Похоже, что extends PureComponent
работает лучше, чем Component
с
shouldComponentUpdate() {
return false;
}
РЕДАКТИРОВАТЬ: Я думаю, что это одна из внутренней оптимизации реакции.
Как видите, у обычных кнопок 637 мс против 47 мс! Вы все еще думаете, что оптимизировать shouldComponentUpdate (или просто PureComponent) не стоит? :)
@Bessonov Это демонстрирует потенциал чистой логики. Да, это может быть очень полезно! Это улучшение в 13 раз. Но я не думаю, что это близко к реальным условиям:
@oliviertassinari, как отличный разработчик, как можно писать такие вещи? Почему вы используете свои личные предположения в качестве аргументов, а не фактов? Думаю, выше я привел достаточно фактов. Я сделал все, чтобы показать это, потому что мне нравится этот проект и я хочу внести свой вклад. Этого не достаточно? Хорошо, тогда еще факты и _no_ предположения.
Только для вас уменьшаю до 10 кнопок. 10! Это означает, что любые 10 компонентов (хуже: контейнеров) из material-ui замедляют работу всего приложения до тех пор, пока оно не станет непригодным для использования! Протестировано на реальном устройстве с пешеходным переходом 21 / хром 51 без дроссельной заслонки:
Обычная кнопка:
PureButton:
Это все еще 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
С чистым:
Без чистого:
Воспроизведение следующее.
@oliviertassinari , вы уверены, что codeandbox все исправит для вашего теста? Потому что мои результаты очень разные:
Без чистого (даже без дросселя медленно):
Чистый (после изменения на true и сохранения я получаю новый URL-адрес дляcodeandbox):
Поскольку он не проверяет изменения контекста, а также заставляет пользователей следить за тем, чтобы все дочерние компоненты также были «чистыми», я не считаю, что это хорошо подходит для этой библиотеки. Эта библиотека должна быть максимально гибкой, и я считаю, что это затруднит ее использование.
Я вижу суть. Но это очень интересный компромисс. С одной стороны, даже 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
перемещен наверх, ура.InputProps={{startAdornment: ...}}
все еще встроен и создает новый объект при каждом рендеринге, что делает невозможным использование PureComponent.classes={{label: classes.runButtonLabel}}
делает невозможным для PureComponent оптимизацию кнопки «Выполнить».Мне лично нравится PureComponent, и я стараюсь использовать его абсолютно везде и оптимизировать вещи, насколько это возможно, чтобы он работал. Но не похоже, что MUI вообще сделан так, чтобы PureComponent работал.
*Props
такие как InputProps
являются фундаментальным шаблоном того, как работает MUI. Это не просто продвинутый способ изменения внутренних компонентов MUI, когда он вам нужен, а то, что регулярно используется в простых случаях использования. Этот шаблон часто делает любой листовой компонент, который обычно можно оптимизировать в чистом режиме, неоптимизируемым.classes={{...}}
также не работает с PureComponent, и это способ добавления любого стиля к вещам в MUI. (И использование classes={classes}
никоим образом не практично, потому что реальный потребитель, вероятно, имеет другое имя класса, чем внутренний класс компонента, и classes
, вероятно, также будет включать классы, предназначенные для стилизации других элементов в том же потребляющем компоненте)Если мы хотим что-то оптимизировать, необходимо решить эти фундаментальные проблемы, иначе простое разрешение людям включить MUI в чистом режиме на самом деле не приведет к большой оптимизации. Я могу думать о двух возможностях.
shallowMemoize
который использует локальные this
и key
(чтобы вы могли использовать его для разных битов данных) который запоминает данные до тех пор, пока они неглубоко равны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>
);
}
Если это рекомендуемый способ использования 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 и сделать этот компонент чистым. Это дает дополнительное преимущество: вы можете добавлять любые свойства, общие для всех ваших флажков. И, поскольку у вас есть общая проблема, связанная с необходимостью отдельного обработчика событий изменения для каждого элемента, мы можем использовать компонент класса и сделать это в компоненте, а не в контейнере списка.
Если мы хотим что-то оптимизировать, необходимо решить эти фундаментальные проблемы, иначе простое разрешение людям включить MUI в чистом режиме на самом деле не приведет к большой оптимизации. Я могу думать о двух возможностях.
@dantman Эти варианты API были сделаны для того, чтобы максимально улучшить DX, пытаясь при этом быть достаточно быстрым.
Вместо того, чтобы пытаться оптимизировать InputProps, классы и т. Д., Мы призываем людей создавать микрокомпоненты для всех своих классов использования.
Да. Шаблон упаковки, безусловно, является рекомендуемым способом настройки библиотеки. Его можно расширить, чтобы применить оптимизацию производительности. Это проще в пользовательской среде, где вариативность использования компонентов намного ниже. Мы могли бы даже добавить в документацию FAQ или раздел руководства по этому поводу.
Да. Шаблон упаковки, безусловно, является рекомендуемым способом настройки библиотеки. Его можно расширить, чтобы применить оптимизацию производительности. Это проще в пользовательской среде, где вариативность использования компонентов намного ниже. Мы могли бы даже добавить в документацию FAQ или раздел руководства по этому поводу.
Ok. В этом случае:
../../../../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. По поводу производительности. У меня есть две важные вещи, но я надеюсь узнать больше:
style = f(props)
(ну, пока не поддерживаем: # 7633). Мы можем реализовать очень эффективное кэширование, сократив стоимость повторных запросов почти до 0%. Я мог бы поработать над этим для офиса, если производительность SSR вредит нашим бизнес-показателям.Спасибо за адреса.
Полностью согласен с 1 и связанным # 4305 выше.
Оптимизация времени компиляции была бы замечательной и, с моей точки зрения, предпочтительнее, потому что она может улучшить первоначальную загрузку и повторную визуализацию.
Другое дело - своего рода документация или пример проекта, как использовать mui (и babel и т. Д.) Для повышения производительности.
Не знаю, можно ли использовать jss-cache .
Конвейер предварительной обработки ISTF + сможет сэкономить несколько мс.
Сб, 19 мая 2018 г., 19:06 Антон Бессонов [email protected] написал:
Спасибо за адреса.
Полностью согласен с 1 и связан # 4305
https://github.com/mui-org/material-ui/issues/4305 выше.
- 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
Результаты в моем браузере:
⚠️ с неуверенностью
общая производительность 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 он может быть немного медленнее:
Мы вполне можем быть ограничены самим 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
(дочерние элементы или свойства)? Если вы имеете в виду, что я должен написать определенный компонент для каждой панели в моем приложении, это невозможно, к сожалению, у меня их слишком много.
@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. Это не только оптимизирует его (что приведет к тому, что несвязанные входные данные не будут повторно отображены), но также будет означать, что код не должен повторять дополнительный шаблон.
Прочитав это, я пришел к выводу, что для оптимизации mui вам нужно создавать более мелкие конкретные компоненты. Это то, что я уже понял и успешно попробовал. Однако я также понял, что нет способа оптимизировать компоненты списка, потому что api контекста изменяет все входные реквизиты.
С Уважением
Хорошо, вот обновленный стресс-тест https://codesandbox.io/s/wz7yy1kvqk
Я согласен с @dantman в этом общем вопросе https://github.com/mui-org/material-ui/issues/10778#issuecomment -449153635 НО я не ожидал такой проблемы с производительностью при таком небольшом количестве компонентов, но терпите мне, поскольку я нашел источник моей проблемы с производительностью.
Ссылаясь на некоторые из предыдущих комментариев в этом потоке, я добавил флажок в стресс-тесте, чтобы удалить все вызовы withStyles
и пришел к выводу, что JSS работает быстро и не является источником проблемы с перфомансом. (как @kof указал на это в https://github.com/mui-org/material-ui/issues/10778#issuecomment-396609276).
Для моего конкретного случая использования я смог точно определить проблему, заключающуюся в том, что каждый ввод формы был повторно отрисован при обновлении формы, хотя фактически изменился только один ввод.
На скриншоте ниже я заключил FormControl и Input в мемоизированный компонент, избегая рендеринга, если значение не изменилось. @dantman на самом деле предложил мне создать определенный компонент для каждого ExpansionPanel
но это гораздо менее общее решение. В качестве примечания, каждая панель все еще перерисовывается, и производительность далека от оптимальной, но на данный момент ее достаточно.
Я думаю, что нет способа избежать такого рода проблем с изменением кода 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 элементов в режиме разработки . Мы можем рассматривать следующие случаи:
Таким образом, накладные расходы на использование Material-UI в режиме разработки над элементом хоста составляют около x4 (разница меньше в производстве!) Просто потому, что мы создаем промежуточные компоненты. Вот почему виртуализация становится важной после рендеринга списка из ~ 100 элементов таблицы. Теперь мы можем немного углубиться в то, почему и что мы можем с этим поделать?
Итак, у нас есть одно преимущество: перенести все компоненты из withStyles в makeStyles. Мы можем выиграть около + 30% производительности (262 / (262-70)) в режиме разработки .
Я выполнил те же тесты в производственном режиме:
Таким образом, миграция с withStyles
на makeStyles
теоретически увеличивает ускорение на 30% в производственном режиме.
Если ты думаешь, что все в порядке, я могу открыть новый выпуск.
@ mkermani144 Если у вас есть конкретный случай, когда Material-UI делает это неправильно, конечно.
Я прочитал обо всех комментариях под этой проблемой. Моя проблема не вписывается ни в одну из упомянутых ранее.
У меня есть компонент List
содержащий некоторые ListItem
s, один из которых выбран и выделен. Когда я нажимаю другой ListItem
чтобы выбрать и выделить его, весь список (содержащий его дочерние элементы) снова перерисовывается.
Я знаю, что эта проблема может показаться такой же, как и в предыдущем комментарии , но это не так; по крайней мере, я так думаю.
Посмотрим на результаты профилировщика React:
Как видите, у меня есть компонент 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-й пункт списка):
Как видите, он работает должным образом, т.е. дополнительных повторных рендеров не требуется (за исключением компонентов 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 .
List
, однако, может стать узким местом производительности, если он достаточно велик, независимо от того, насколько он оптимизирован. Разве мы не должны предоставить пример, показывающий использование react-window
или react-virtualized
с компонентом List
, как тот, который есть в Table
docs?Разве мы не должны предоставить пример, показывающий использование response-window или react-virtualized с компонентом List, как тот, который у нас есть в документации к таблицам?
Было бы здорово: +1:
Fwiw Я создал приложение для чата, и оно должно отображать большой список контактов. Я столкнулся с той же проблемой
@ mkermani144 имел.
@ 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:
Из-за этого все мои дочерние компоненты (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 мс
Случай 2 (пользователь нажимает кнопку «Добавить в заказ» в диалоговом окне)
Длительность рендеринга: 356 мс
Я уверен, что совершаю здесь некоторую ошибку новичка, поэтому приветствуются любые рекомендации.
Поскольку 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;
Без <Box>
es:
@mankittens Вы можете сохранить компонент Box, используя стилизованные компоненты в качестве движка стилей. Производительность была бы намного лучше. В ближайшем будущем он должен улучшиться с JSS https://github.com/mui-org/material-ui/pull/16858.
Закрываю этот вопрос. Нам нужен специальный отчет о производительности для каждой потенциальной области улучшения, а не общая тема.
Самый полезный комментарий
@oliviertassinari, как отличный разработчик, как можно писать такие вещи? Почему вы используете свои личные предположения в качестве аргументов, а не фактов? Думаю, выше я привел достаточно фактов. Я сделал все, чтобы показать это, потому что мне нравится этот проект и я хочу внести свой вклад. Этого не достаточно? Хорошо, тогда еще факты и _no_ предположения.
Только для вас уменьшаю до 10 кнопок. 10! Это означает, что любые 10 компонентов (хуже: контейнеров) из material-ui замедляют работу всего приложения до тех пор, пока оно не станет непригодным для использования! Протестировано на реальном устройстве с пешеходным переходом 21 / хром 51 без дроссельной заслонки:
Обычная кнопка:
PureButton:
Это все еще 8-кратное улучшение! Это огромная! Представляете, насколько это важно на мобильном устройстве?
Я использовал кнопку, потому что это один из самых простых компонентов! Это показывает, что material-ui не работает с точки зрения производительности. Теперь представьте себе такие компоненты контейнера, как AppBar, Toolbar, List, Drawer! Это еще хуже! Вы очень быстро получаете до 20 компонентов / контейнеров на каждой странице. И поскольку у вас не заканчивается медлительность на вашем мощном настольном компьютере / Mac, это не значит, что material-ui не невероятно медленный.
Покажи мне пример на codeandbox. Я не понимаю, почему это должно происходить. Я уверен, что это происходит только при неправильном использовании компонентов реакции. И официальный пример показывает неправильное использование, потому что кажется, что response-intl не применяет подписчиков контекста . Но есть много других компонентов, которые согласованы с руководящими принципами реагирования и передовыми практиками, чтобы оставаться эффективными.
Кстати: WithStyles потребляет до 2,27 мс для кнопки на мобильном устройстве. 8 компонентов и вы под 60fps.