Автор недавно представил библиотеку анимации в проекте Vue, но обнаружил, что производительность немного ненормальная. ЦП, используемый в проекте, примерно в 3,5 раза больше, чем у демонстрационной страницы. Я удалил все другие мешающие вещи в проекте. Но ЦП просто не может снизиться.Как показано на рисунке ниже, нормальный диапазон колеблется в районе 2,1%:
Но когда его вводят в проект, становится колебание около 7%:
Может ли это быть из-за того, что вложенность html слишком глубокая, что усложняет Layout и другие вычисления, поэтому увеличивается нагрузка на процессор?Автор попытался упростить структуру DOM и добавить методы изоляции Layout, такие как contains:strict, но это не сработало. Таким образом, это может быть только проблема выполнения JS, которую можно изучить с помощью инструментов разработки Chrome.
Как показано ниже:
Плотные строки выше — это обратные вызовы requestAnimationFrame, увеличьте его, а затем просмотрите обратный вызов, чтобы сравнить разницу между демонстрационной страницей и страницей Vue, как показано на следующем рисунке:
Здесь хорошо видна разница: время выполнения каждого обратного вызова в demo.html составляет около 0,3 мс, в то время как время выполнения обратного вызова проекта Vue составляет около 0,8 мс, что почти в 3 раза быстрее, а стек вызовов намного Глубже. Что это за дополнительные вещи? Взгляните повнимательнее:
Эти штуки есть во Vue, то есть сеттеры во Vue, а некоторые обратные вызовы тоже содержат геттеры во Vue:
В это время я вдруг понял, что из-за того, что геттер/сеттер переменной был переписан во Vue, получение свойства или переписывание свойства занимало много времени, из-за чего поднимался процессор. Причина, по которой Vue переписан, заключается в том, что переменная библиотеки анимации используется как свойство this в компоненте в коде, как показано в следующем коде:
import Player from 'player.js';
export default {
data: {
return {
player: new Player()
};
}
};
Затем Vue пройдёт по объекту игрока и добавит сеттеры/геттеры ко всем свойствам, как показано на следующем управляющем выводе:
Здесь Ir.set — это скриншот из производительности выше, что замедляет установку переменной Ii. Здесь мы заметили деталь, консоль Chrome будет напрямую печатать Object, который не покрывает сеттер/геттер, и если он установлен, он будет заменен на "(...)", а затем ждать, пока вы не нажмете, чтобы получить Отображается текущее значение.
Как видно из исходного кода Vue, Vue определяет установщик и получатель свойства для переменных-членов:
// 代码有所删减
function defineReactive?1 (obj, key, val) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
// 从源码也可以看到,可以把obj的configurable置为false,Vue便不会设置getter和setter
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
var getter = property && property.get;
var setter = property && property.set;
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
dep.notify();
}
});
}
Чтобы сделать некоторые уведомления, когда пользователь устанавливает значение, чтобы достичь цели, управляемой данными. Но в то же время это также может вызвать проблемы с производительностью, в этом примере время вызова увеличивается примерно на 0,3 мс. На самом деле это время почти ничтожно, но так как этот пример нужно запускать в requestAnimationFrame, то он вызывается 60 раз в секунду, что относительно часто.Исходное время было всего 0,2 мс, но теперь оно увеличилось на 0,3 мс из-за к этому сеттеру/геттеру. , более чем в два раза больше обычного времени, поэтому ЦП увеличивается.
Зная причину, можно решить проблему Текущее решение состоит не в том, чтобы рассматривать переменную player как свойство-член в this, а в том, чтобы получить ее снаружи, как показано в следующем коде:
import Player from 'player.js';
let player = new Player();
export default {
data: {
return {
};
}
};
(Дополнительно: вы также можете видеть из исходного кода Vue, что установка для настраиваемого свойства объекта значения false также может решить проблему.)
В это время загрузка ЦП упала с 7% до примерно 4%, почти вдвое, как показано на следующем рисунке:
Глядя на стек вызовов сеттера в Performance, больше нет, как показано на следующем рисунке:
Но CPU по-прежнему в два раза больше, чем у демо-страницы (2% и 4%), а пока продолжаем проверять стек вызовов и обнаруживаем, что время вызова функции одной ji в два раза больше, чем другой:
Эти две функции подтверждаются, когда вы нажимаете на исходную панель, чтобы посмотреть код. Единственная разница здесь может заключаться в том, что demo.html использует сжатый код, а локальный проект несжатый. Если вы упаковываете и сжимаете его , вставьте его в тестовую среду, вы можете увидеть, что время процессора почти одинаковое:
Объединение нескольких операторов в один в сжатом коде также должно повысить производительность.
Подводя итог, эта статья не означает, что есть проблема с реализацией Vue, но необходимо обратить внимание на влияние сеттера/геттера на производительность, особенно в обратном вызове анимации. -время операции практически ничтожно, и его не следует игнорировать, нужно об этом заботиться. Кроме того, просто установка сеттера/геттера в анимации не обязательно вызовет внезапную загрузку ЦП.Это также зависит от того, что вы сделали в сеттере/геттере.В Vue вы можете видеть, что его стек вызовов относительно глубокая Да, может быть больше вещей, которые нужно оценивать внутренне.
Кроме того, это исследование заставляет задуматься над интересным вопросом,? Если я пишу цикл для записи, использование ЦП должно быть 100% (он заполняет одно ядро), как показано в следующем коде:
let now = Date.now();
// 跑个50s
while (Date.now() - now < 50000);
В это время загрузка ЦП составляет 100%:
Если я позволю ему заснуть на 50 мс, то просушу еще 50 мс, попеременно, как показано в коде ниже:
function sleep (time) {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}
let now = Date.now();
async function start () {
while (Date.now() - now < 50000) {
// 睡50ms
await sleep(50);
let current = Date.now();
// 干50ms
while (Date.now() - current < 50);
}
console.log('end');
}
start();
В это время загрузка ЦП будет колебаться около 50%, как показано ниже:
Разве это не интересно?