В этой статье вы увидите, что я наконец пришел к выводу, что Mobx превзошел Redux. Но очевидно, что этот вывод односторонний, даже предвзятый, потому что я выбрал только одну сцену, чтобы протестировать обе. Вероятно, верно обратное, Mobx лучше Redux только в этом сценарии, который я тестировал, но хуже Redux во всех сценариях, которые я не тестировал, это возможно. Никогда не принимайте близко к сердцу такие вещи, как оценки производительности. Разве «Мастер Лу» также не имеет прозвища «Мастер развлечений».
Цель этой статьи не в том, чтобы позволить этим двоим сражаться насмерть, а в том, чтобы исследовать, каковы могут быть причины преимуществ и недостатков в процессе сравнения производительности, и что мы можем извлечь из этого.
Если вернуться на 10 000 шагов назад, даже если Redux работает немного хуже, это не повредит. Когда мы оцениваем фреймворк или выбираем технические характеристики продукта, производительность — это только один аспект. Например, механизм естественного поиска событий Redux может помочь нам легко отследить состояние.Если вам нужен такой бизнес-сценарий в вашем продукте, то Redux, конечно, лучший выбор. Обычно нет большой разницы в производительности ниже определенного порога.
По сравнению с кем и как
Давайте посмотрим на Mobx из stackoverflow.интересный вопрос производительностиНачинать
Спрашивающий сделал тест, собираясьobservable.array
В декорированном массиве (собственная структура данных Mobx)push
200 элементов, посчитайте общее затраченное время и сравните с нативной операцией. В результате подход Mobx занял в общей сложности 120 мс, в то время как нативная операция заняла менее 1 мс. Означает ли это, что производительность Mobx ужасна?
Теоретически метод проверки спрашивающего верен, и результаты проверки верны. Но проблема в том, что чисто численное сравнение несправедливо, хотя исходный массивpush
Метод быстрее, но он не может обеспечить односторонний поток данных, не может обеспечить управление состоянием, не так ли? В то же время Mobx также может сотрудничать с React для оптимизации рендеринга компонентов. Таким образом, мы можем не только учитывать размер стоимости, но и учитывать прибыль и убытки от общих интересов. Mobx в 120 раз медленнее в этой операции.Во-первых, разница в 120 мс практически незаметна для пользователей.Во-вторых, это обеспечивает удобство для наших проектов разработки и экономит затраты на дальнейшее обслуживание.Вы должны знать, что эти затраты основаны на Рассчитано на человека и месяц .
В первые дни моей работы по оптимизации я привык использовать инженерные индикаторы, такие как время DOMContentLoaded, время onLoad и более мягкий индекс скорости. Но в данный момент я предпочитаю использовать индикаторы делового характера, потому что вы хотите прояснить один вопрос: действительно ли инженерные индикаторы положительно коррелируют с бизнес-индикаторами? Если время onLoad велико, действительно ли увеличится показатель отказов? Теоретически да, но не обязательно, наоборот, если вы капризничаете, то можете увеличить время onLoad, но показатель отказов упадет, главное, чтобы вышеуказанный фолд-контент был достаточно быстрым и доступным
В конце концов, технологии для бизнеса. Последняя газета, которую я прочиталSeven Rules of Thumb for Web Site ExperimentersПример из приведенного выше завершает этот раздел. Короче говоря, я просто хочу подчеркнуть два момента: 1) не измеряйте слепо и абсолютно качество исполнения; 2) больше думайте с точки зрения бизнеса
В Bing мы используем несколько показателей производительности для диагностики, но Нашей ключевой метрикой, связанной со временем, является время до достижения цели (tts) [24]. Обход вопросов измерения. Для поисковой системы наша цель Чтобы пользователи могли выполнять задачу быстрее. Пользователь БЫСТРЕЕ НАЖИМАЕТ НА РЕЗУЛЬТАТ, из которого они не возвращаются Успешным кликом считается не менее 30 секунд. ТТС КАК КАК КАК КАК Метрика хорошо отражает воспринимаемую производительность: ЕСЛИ ИТ улучшится, тогда Важные области страниц отображаются быстрее, чтобы пользователи могли Интерпретируйте страницу и нажмите Быстрее.Этот относительно простой показатель Не страдает эвристикой, необходимой для многих показателей ITRICS. Он очень устойчив к изменениям и очень чувствителен. Недостаток в том, что ИТ работает только для кликабельных элементов. ГДЕ у поисковой выдачи есть ответ (например, для запроса «Время»), пользователи МОГУТ Будьте удовлетворены и оставьте страницу heling.
Сравнение производительности
Причина, по которой необходимо сравнение, заключается в том, что я ищу варианты технологий для своего следующего проекта. Важный пользовательский сценарий в новом проекте аналогичен Photoshop, с большой областью в центре экрана для перетаскивания и размещения объектов. Когда элемент выбран, окружающие панели атрибутов показывают различные связанные атрибуты элемента.Когда элемент перетаскивается в режиме реального времени, отображаемое содержимое панели также должно изменяться в режиме реального времени.
Этот сценарий можно абстрагировать следующим образом: несколько объектов подписываются на свойства одного и того же объекта и отображают их. Я имитирую этот сценарий, реализуя секундомер отображения в реальном времени с использованием Mobx и Redux соответственно.
Я всегда был против размещения в статье всего абзаца кода, но в этот раз нет возможности.Для обеспечения полноты прочтения вроде бы ни одна часть кода не может быть опущена, поэтому версии, написанные на оба фреймворка полностью выложены.
Мобкс версия:
class StopWatch {
@observable
currentTimestamp = 0;
@action
updateCurrentTimestamp = value => {
this.currentTimestamp = value;
};
}
const stopWatch = new StopWatch();
@inject("store")
@observer
class StopWatchApp extends React.Component {
constructor(props) {
super(props);
const stopWatch = this.props.store;
setInterval(() => stopWatch.updateCurrentTimestamp(Date.now()));
}
render() {
const stopWatch = this.props.store;
return <div>{stopWatch.currentTimestamp}</div>;
}
}
ReactDOM.render(
<Provider store={stopWatch}>
<div>
<StopWatchApp />
</div>
</Provider>,
document.querySelector("#app")
);
Редукс версия:
const UPDATE_ACTION = "UPDATE_ACTION";
const createUpdateAction = () => ({
type: UPDATE_ACTION
});
const stopWatch = function(
initialState = {
currentTimestamp: 0
},
action
) {
switch (action.type) {
case UPDATE_ACTION:
initialState.currentTimestamp = Date.now();
return Object.assign({}, initialState);
default:
return initialState;
}
};
const store = createStore(
combineReducers({
stopWatch
})
);
class StopWatch extends React.Component {
constructor(props) {
super(props);
const { update } = this.props;
setInterval(update);
}
render() {
const { currentTimestamp } = this.props;
return <div>{currentTimestamp}</div>;
}
}
const WrappedStopWatch = connect(
function mapStateToProps(state, props) {
const {
stopWatch: { currentTimestamp }
} = state;
return {
currentTimestamp
};
},
function(dispatch) {
return {
update: () => {
dispatch(createUpdateAction());
}
};
}
)(StopWatch);
ReactDOM.render(
<Provider store={store}>
<div>
<WrappedStopWatch />
</div>
</Provider>,
document.querySelector("#app")
);
Обратите внимание, что в приведенной выше версии кода Redux каждыйStopWatch
Подпишитесь непосредственно на статус CurrentTimeStamp в Магазине. В задней части мы попробуем другой способ
Если вы запустите две версии кода по отдельности, вы не заметите никакой разницы. Но если мы поместим окончательный рендеринг в Mobx, который нужно отобразить<StopWatchApp />
Экземпляр и окончательный рендеринг в Redux<WrappedStopWatch />
Экземпляр расширен до 20 (так же есть 20 подписок на состояние магазина):
ReactDOM.render(
<Provider store={store}>
<div>
<WrappedStopWatch />
<WrappedStopWatch />
<WrappedStopWatch />
<WrappedStopWatch />
<WrappedStopWatch />
// ...省略后面的15个
</div>
</Provider>,
document.querySelector("#app")
);
Вы почувствуете, что Redux явно застрял (это видно невооруженным глазом, нет необходимости использовать точное время, чтобы показать разницу), или скорость изменения значительно ниже, чем у версии Mobx. Здесь не будут размещаться видео или гифки. Вы можете сразу увидеть, когда запустите код
Почему мы можем увидеть подсказку через инструменты разработки Chrome.Это выполнение запущенного скрипта:
Обратите внимание, что самые трудоемкие прослеживаются в исходном коде нижеEvent
операции, прослеженной до исходного кода, мы можем видеть, что ее стек вызовов в основном происходит изdispatch
:
Тем не менее, у нас есть основания подозревать, что Reduxdispatch
Это приведет к потере производительности (блин, это основной механизм). С тем же успехом мы могли бы сначала сделать предположение: в приведенном выше коде, поскольку мы используем 20 компонентов независимого хранилища подписки, мы косвенно используемdisaptch
, что приводит к снижению производительности. Далее мы хотим проверить правильность этого предположения, принцип очень простой, добиваемся того же эффекта, то есть отображаем 20 секундомеров на странице одновременно, но используем только одну подписку - для подписки используем родительский контейнер в хранилище, а затем передать состояние подсборке. Магазинную часть модифицировать не нужно, а компонентная часть модифицируется следующим образом:
const StopWatch = ({ currentTimestamp }) => {
return <div>{currentTimestamp}</div>;
};
class Container extends React.Component {
constructor(props) {
super(props);
const { update } = this.props;
setInterval(update);
}
render() {
const { currentTimestamp } = this.props;
return (
<div>
<StopWatch currentTimestamp={currentTimestamp} />
// 省略剩下的 19 个
</div>
);
}
}
const WrappedContainer = connect(
function mapStateToProps(state, props) {
const {
stopWatch: { currentTimestamp }
} = state;
return {
currentTimestamp
};
},
function(dispatch) {
return {
update: () => {
dispatch(createUpdateAction());
}
};
}
)(Container);
ReactDOM.render(
<Provider store={store}>
<div>
<WrappedContainer />
</div>
</Provider>,
document.querySelector("#app")
);
Этот код подтверждает нашу идею После модификации программа стала очень быстрой, дойдя до той же скорости отображения, что и Mobx. Это также подтверждает нашу гипотезу,dispatch
Там действительно есть штраф за производительность, но страшная вещьdispatch
Это воля механизма событий Redux. Здесь мы не продолжаем исследовать, почемуdispatch
причина замедления
Но имейте в виду, что рендеринг через родительский контейнер — это не нормальная оптимизация.
В статье почти годичной давности«Оптимизация производительности React + Redux (1): теория»Выше я упомянул, что унифицированный рендеринг списка родительским контейнером на самом деле является последним средством. Из-за неизменяемых данных после отображения определенного содержимого данных в списке будет повторно отображен весь список, включая те, которые не были изменены.
Мое предложение состоит в том, что когда вы визуализируете список, разделите структуру данных списка на две части: список идентификаторов и словарь элементов: родительский контейнер отвечает только за отображение внешнего контейнера каждого элемента в соответствии со списком идентификаторов, и каждый Конкретное содержание элемента заключается в том, что каждый компонент проекта напрямую обращается к хранилищу для получения:
class App extends Component {
render() {
const { ids } = this.props;
return (
<div>
{ids.map(id => {
return <Item key={id} id={id} />;
})}
</div>
);
}
}
Еще один пример теста производительности Mobx vs Redux от автора Mobx Мишеля Вестстрата (ну, это звучит несправедливо), из егоtwitter
Исходный код этого теста находится по адресуGitHub.com/M западный уровень блуда…
Тест показывает время, необходимое для одной и той же операции (изменение задачи или добавление задачи в todo mvc) в Mobx и Redux (еще одна переменная — количество задач). Как видно из графика, в любом случае Mobx занимает меньше всего времени.
Почему Mobx быстрый?
Автор этого вопроса Mobx находится вBecoming fully reactive: an in-depth explanation of MobXВ этой статье это уже очень ясно объяснено, здесь мы кратко выделим несколько моментов.
Возьмем в качестве примера приложение Redux, вам нужно использовать механизм подписки для решения проблемы синхронизации данных, например, данные в представлении будут несовместимы с данными в магазине (или селекторе). Но по мере роста приложения управление подписками будет становиться все более и более сложным.Например, вы можете подписаться на данные, которые больше не используете, или подписаться на данные, которые вам не нужны, или забыть подписаться на данные, которые вам нужны. В React чрезмерные подписки могут привести к ненужному повторному рендерингу компонентов. Обратите внимание, что даже если ваша подписка предназначена для данных, которые вам нужно использовать только при определенных условиях, это считается превышением лимита подписки.
Таким образом, очень важная философия дизайна, лежащая в основе Mobx, такова: минимальный, согласованный набор подписок может быть достигнут только в том случае, если подписки определены во время выполнения.
Метод очень простой, все данные не кэшируются, а все вычисляются через деривацию (если вы понимаете Mobx, вы должны знать концепцию деривации, которая относится к вычисляемому значению и реакциям). Но не будет ли это стоить дорого? Нет, наоборот, очень эффективно. Mobx не вычисляет все производные значения, а вычисляет те, которые в настоящее время находятся в наблюдаемом состоянии (или, что более общеизвестно, используется в настоящее время или является видимым).
Например, такой как следующий код:
class Person {
@observable firstName = "Michel";
@observable lastName = "Weststrate";
@observable nickName;
@computed get fullName() {
return this.firstName + " " + this.lastName;
}
}
// Example React component that observes state
const profileView = observer(props => {
if (props.person.nickName)
return <div>{props.person.nickName}</div>
else
return <div>{props.person.fullName}</div>
});
Зависимости, которые мы получаем из кода, следующие:
в то время как на самом деле для Mobx это упростило бы до
Это, естественно, уменьшает количество вычислений.
Для меня автор объясняет оптимизацию автора. Основной практикой я этого не делал, и я не рассматривал такую программу. Так что не вы определяете, насколько это может улучшить, я надеюсь извлечь уроки из этой идеи в будущем.
конец
Как было сказано в начале, в этой статье роль рассматривается как руководство, просто беглый взгляд на сравнение производительности. Кроме того, у меня все еще есть оговорки по поводу использования технологии Mobx в сценарии проекта, описанном в статье.Эффективность интуиции все еще не высока, и я продолжу изучать более эффективные способы.
использованная литература
- Seven Rules of Thumb for Web Site Experimenters
- Becoming fully reactive: an in-depth explanation of MobX
Эта статья также была опубликована в моем личномЗнайте переднюю колонку, приветствую всех, чтобы обратить внимание
Эта статья не удовлетворена письмом, он не работает