I recently in a Vue program introduces an animation library, but found that performance is a bit unusual, projects in use of the CPU is about 3.5 times in a demo page, I’ve put the project in all other interference to delete everything, but the CPU is to fall down, as shown in the figure below, the normal range is in 2.1% fluctuations:
But when it comes to projects, it fluctuates around 7% :
The author tried to simplify the DOM structure and add contain: strict and other Layout isolation methods, but it did not work. So it’s just a JS execution problem, which can be investigated in Chrome DevTools Performance.
As shown below:
The thick lines above are callbacks to requestAnimationFrame. Zoom in and see a callback to compare the demo page to the Vue page, as shown below:
The difference is obvious here. Demo.html takes about 0.3ms per callback, while Vue project takes about 0.8ms per callback, nearly three times faster, and the call stack is much deeper. What are these extra things? Take a closer look:
These things are in Vue, which is the setter in Vue, and part of the callback contains the getter in Vue:
Because Vue overwrites the getter/setter of a variable, it takes longer to get a property or overwrite a property, causing the CPU to increase. The reason for the Vue rewrite is that the animation library variable is the property of this in the component, as shown in the following code:
import Player from 'player.js'; export default { data: { return { player: new Player() }; }};Copy the code
Vue then iterates through the Player object, assigning setters/getters to all properties, as shown in the following control print:
The ir. set shown here is the screenshot from Performance above, which is why setting variable Ii is slow. Here we notice that the Chrome console will print objects that do not override setters/getters, and those that do will be printed with “(…). “Instead, wait until you click to get its current value displayed.
Vue defineProperty setters and getters for member variables
Function defineReactive? 1 (obj, key, val) { var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); // The 64x signals of obJ can be configured as false. If (property && property.64x === false) {return} // Handle for pre-defined, works without any additional control 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(); }}); }Copy the code
So that users can set values with some notification, thus achieving data-driven purposes. But it can also cause performance problems, in this case around 0.3ms of call time. In fact, this time is almost negligible, but since this example needs to run in requestAnimationFrame, 1s is called 60 times, which is quite frequent. Originally, it was only 0.2ms. Now, thanks to the setter/getter, 0.3ms has been added. More than twice as long as normal, so the CPU went up.
The solution is not to treat the player variable as a member property inside this, but to put it outside, as shown in the following code:
import Player from 'player.js'; let player = new Player(); export default { data: { return { }; }};Copy the code
Vue provides a different information system, and sets the object property to false.
At this point, the CPU drops from 7% to around 4%, which is almost half way down, as shown below:
If you look at setters in Performance, the call stack is gone, as shown below:
However, the CPU is still twice as long as the demo page (2% and 4%). If you look at the call stack, you will find that one ji function is called twice as long as the other:
The only difference here is that demo. HTML uses compressed code, while the local project is uncompressed. If you pack it up and put it in the test environment, you can see that the CPU time is almost the same:
Compression code that combines multiple statements into a single statement should also improve performance.
Finally, this article is not saying that there is a problem with the implementation of Vue, but it is important to be aware of the performance impact of setter/getter, especially in an animation callback, which is generally negligible for one-off operations and should not be a concern. Depending on what you’re doing in the setter/getter, you can see in Vue that the call stack is a little bit deeper, and there may be a lot of things that need to be determined internally.
Another interesting question this study raises is, how do you keep CPU utilization at 50%? If I write a for write loop, the CPU usage must be 100% (it runs a full core), as shown in the following code:
let now = Date.now(); // run the 50s while (date.now () -now < 50000);Copy the code
The CPU usage is 100% :
If I let it sleep for 50ms, and then dry for another 50ms, again and again, the code looks like this:
function sleep (time) { return new Promise(resolve => { setTimeout(resolve, time); }); } let now = Date.now(); Async function start () {while (date.now () -now < 50000) {// sleep 50ms await sleep(50); let current = Date.now(); While (date.now () -current < 50); } console.log('end'); } start();Copy the code
At this point, CPU usage fluctuates around 50%, as shown below:
Isn’t that interesting