Pull to refresh

Comments 19

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

Стоит отметить, что v-show не подойдет, если компонентов одновременно будет много, но они будет достаточно легкими (большое кол-во DOM-элементов на пользу не будет).
Также keep-alive не подойдет для случая, когда один и тот же компонент используется с разными данными, по крайней мере не без костылей (до сих пор issue висит открытым, чтобы можно было использовать key в include).
Также keep-alive не подойдет для случая, когда один и тот же компонент используется с разными данными, по крайней мере не без костылей (до сих пор issue висит открытым, чтобы можно было использовать key в include).

Я честно говоря не совсем понял что имелось ввиду в статье. Дело в том, что vue-router по-дефолту ведет себя лениво и не пересоздает вьюхи, а реюзает их. Чтобы он пересоздавал нужно ему `key` указывать. stackoverflow.com/questions/52847979/what-is-router-view-key-route-fullpath см. ответ

Вот простой пример:



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


Сравните это со вторым примером:



Здесь вы увидите, что каждый компонент создаётся перед первым переходом, но не разрушается. И не создаётся заново при повторном переходе. Компонент сохраняет своё состояние когда вы уходите на другой маршрут.

Да, верно. Мой кейс, видимо надо уточнить — дело касается переходов по маршрутам, использующим один и тот же компонент
codepen.io/mbeloshitsky/pen/povLRey
Object.defineProperty(itemData, 'data', {
    // Отмечаем поле как "не-реактивное"
    configurable: false,
    value: item
})

не проще ли воспользоваться Object.freeze?
Object.freeze работет только поверхностно и не затронет вложенные объекты
Геттер следует кешировать, если он возвращает массив, и обращаемся к нему через .map, .filter или .forEach?
computed: {
  arr() { return [1, 2, 3, 4, 5] },
  arr2() { return this.arr.map(x => x*2) },
}

Нет. Вычисляемое свойство имеет смысл кэшировать только тогда когда оно вызывается в цикле. В вашем случае this.arr будет вызываться только один раз.

За подборку спасибо, но стоило расписать всё поподробнее. Несколько моментов для примера:

Функциональный компонент компилируется в простую функцию и не имеет локального состояния. За счет этого его производительность намного выше
А по ссылке написано всё наоборот. Вы тогда внесите ясность, типа «сейчас надо делать так, но в следующей версии всё поменяется и будет по-другому».

Каждый раз обращаясь к реактивным данным Vue запускает некоторую логику, чтобы определить как и к каким данным вы обращаетесь построить зависимости и так далее.
При инициализации системы реактивности и при вызове сеттера он что-то такое делает, да. А при простом обращении почему?

Представим, что вам нужно сохранить в хранилище большой массив данных

Это значит, что обработка большого массива заблокирует ваш интерфейс на всё время работы.
Когда мы от добавления успели перейти к обработке? И почему обработка заблокирует интерфейс? В .Net, к примеру, я спокойно могу написать в UI-потоке что-то вроде
for (int i = 0; i < 1000000; i++) {arr[i] += i;}
и ничего нигде не заблокируется, даже не лагнёт.
Также непонятно, на чём основано решение, JobQueue и requestAnimationFrame — это какие-то стандартные вещи или нет, что они делают?

Имеем похожую задачу: мы добавляем в хранилище массив очень больших объектов с кучей уровней вложенности
Тут проблема относится к Vue, а не конкретно Vuex.
Что вообще за «configurable: false» и как оно работает? Гугл выдаёт feature request, который непонятно чем закончился. И код — это конечно хорошо, но ещё лучше словами пояснить, что происходит.
А по ссылке написано всё наоборот. Вы тогда внесите ясность, типа «сейчас надо делать так, но в следующей версии всё поменяется и будет по-другому».

В данный момент функциональный компонент это объект с полем рендер функции


export default {
  functional: true,
  render(createElement, context) {
    return createElement('button', 'Click me');
  }
};

В следующей версии он будет упрощен до обычной рендер-функции


const FunctionalComp = props => {
  const theme = inject(themeSymbol)
  return h('div', `Using theme ${theme}`)
}

Не совсем уверен, что вы имели в виду говоря "всё наоборот".


При инициализации системы реактивности и при вызове сеттера он что-то такое делает, да. А при простом обращении почему?

Как я и написал — чтобы построить дерево зависимостей. Другими словами он вызывает гетеры у реактивных свойств, и запоминает что при вычислении result было вызвано свойство base, а значит при изменении base нужно повторно вычислить result.


И почему обработка заблокирует интерфейс?

Потому что так работает JavaScript. В JavaScript нет многопоточности. Если упростить, то вся ваша вкладка работает в одном потоке. Это значит что никакие две задачи не могут исполняться параллельно. И если вы запустите какую-то долгую задачу на JS, то всё остальное будет заблокировано. Попробуйте на любой странице запустить долгий цикл, например:


for (let i = 0; i < Number.MAX_SAFE_INTEGER; i++) {}

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


Также непонятно, на чём основано решение, JobQueue и requestAnimationFrame — это какие-то стандартные вещи или нет, что они делают?

window.requestAnimationFrame() — MDN
splitArray — абстрактная функция которая разбивает массив на части указанного размера и возвращает массив масивов.
JobQueue — абстрактный класс, который принимает какие-то колбеки (задания), и выполняет их один за одним.
Различных реализаций в интернете масса.


Что вообще за «configurable: false» и как оно работает? Гугл выдаёт feature request, который непонятно чем закончился

Object.defineProperty() — MDN


Или как указали в коментариях выше, можно использовать Object.freeze()

Всё наоборот, в смысле "{ functional: true } option removed" и "Functional components in v3 should be used primarily for simplicity, not performance". Т.е., насколько я понимаю из описания, не будет никаких функциональных компонентов и не будут они улучшать производительность, но можно использовать функции, если вдруг есть такое желание.

Как я и написал — чтобы построить дерево зависимостей. Другими словами он вызывает гетеры у реактивных свойств, и запоминает что при вычислении result было вызвано свойство base, а значит при изменении base нужно повторно вычислить result.
Так зачем это делать каждый раз при обращении к свойству? Ну ладно, один раз в начале построили дерево зависимостей и всё, дальше пользуемся полученными результатами.

Если упростить, то вся ваша вкладка работает в одном потоке.
Так я и говорю про UI-поток (в случае с JS он же и единственный). Неужели и правда алгоритм O(n) настолько долго выполняется, что можно успеть невооружённым глазом заметить лаги? И по-прежнему непонятно, откуда взялись долгие обработки, если мы просто добавляем массив в хранилище (внутри commit наверно что-то похожее на state.items = value).

Или как указали в коментариях выше, можно использовать Object.freeze()
Мне кажется, лучше тогда вообще не допускать попадания глубоких объектов в data или хранилище, а то это оборачивание костыльно выглядит (для эксперимента, попросите кого-нибудь не в теме попробовать догадаться, что тут происходит и зачем).
Ну ладно, один раз в начале построили дерево зависимостей и всё, дальше пользуемся полученными результатами.

Года три назад я, вроде бы, также пользовался такой оптимизацией на долгих вычислениях с большим количеством данных. Но эксперимент показал, что этого делать уже не нужно — console.log в фидле вызывается один раз, даже не смотря на то, что обращение к свойству происходит в цикле.

Так я и говорю про UI-поток (в случае с JS он же и единственный). Неужели и правда алгоритм O(n) настолько долго выполняется, что можно успеть невооружённым глазом заметить лаги? И по-прежнему непонятно, откуда взялись долгие обработки, если мы просто добавляем массив в хранилище (внутри commit наверно что-то похожее на state.items = value).

Дело не в алгоритме O(n). Тут логика в том, что когда мы присваиваем свойству длинный массив, все элементы которого должны быть отрисованы (а это могут быть большие и сложные объекты (например GeoJSON) с большими и сложными шаблонами), то вью сперва сравнит их по ключу (чтобы не отрисовывать то, что уже отрисовано), а вот все новые элементы синхронно начнёт добавлять в DOM. Если новых элементов массива очень много, то и отрисовка DOM-дерева для них также займёт приличное время (зависит ещё от железа, конечно). Вот пока DOM-дерево создаётся, пока браузер всё это не отрисует — это и есть время лага, вкладка браузера в этот момент просто зафризится.
Суть оптимизации в этом и состоит — разбить внесение изменений в DOM-дерево на небольшие кусочки. Да, отрисовка может получиться с рывками, но это всё-равно лучше, чем полностью фризить страницу.
Но эксперимент показал, что этого делать уже не нужно — console.log в фидле вызывается один раз, даже не смотря на то, что обращение к свойству происходит в цикле

Вы правы, Vue кэширует значения вычисляемых свойств. И при повторном обращении не вычисляет их заново.


НО! Под капотом, он всё равно выполняет некоторую логику, при каждом обращении.


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


https://jsfiddle.net/kozack/3a78q4vj/13/


Если посмотреть в консоли, то вы увидите, что staticData вычисляется только один раз, не смотря на многочисленные обращения. И увидите, что optimized вычисляется приблизительно в 10 раз быстрее, благодаря тому, что обращения к staticData сведены к минимуму.

Вы абсолютно правы, спасибо за ваш пример!
Я уже после своего комментария понял, что не то проверял)

Удивительно то, что скорость вычислений падает аж в 18-20 раз. Значит не зря я несколько лет назад что-то подобное делал.
Прям хоть правило для eslint'а пиши, чтобы оно заставляло писать что-то вроде
const { anotherComputed } = this;
в начале методов и других computed'ов, если обращение к ним просходит в циклах или методах массивов forEach/map/reduce/etc.
Если ваше приложение построено так, что зависит только от объекта верхнего уровня и не ссылается на реактивные данные где-то на несколько уровней ниже

Не очень понятно, как данные будут изменять в компоненте, если изменились в родителе и вы отключили реактивность?
Sign up to leave a comment.

Articles