This article explores many aspects of how to optimize JavaScript performance in the ever-changing JavaScript ecosystem. We will follow the principle of "tools, not rules" and try to avoid using too much JavaScript jargon. Due to limited space, all aspects of JavaScript performance optimization cannot be covered, be sure to read the reference link and delve into it yourself.
Before we dive into the details, let's first understand this question from a more macro perspective: What is high-performance JavaScript and how does it fit into a broader web performance metric?
Key Points
Background settings
First of all, we need to be clear: If you only test on desktops, you exclude more than 50% of users.
This trend will only continue to evolve as emerging market users access the internet primarily through Android devices priced under $100. The era of desktops as the main device for accessing the Internet has ended, and the next batch of one billion Internet users will access your website mainly through mobile devices.
Testing in device mode with Chrome DevTools is not a substitute for testing on real devices. Using CPU and network current limiting helps, but it is essentially different. Please test it on real devices.
Even if you are testing on a real mobile device, you may be testing with your brand new $600 flagship phone. The problem is, this is not the device your users have. The median device is similar to the Moto G1 – a device with less than 1GB of memory, very weak CPU and GPU.
Let's see how it performs when parsing average JS packages.Addy Osmani: Average time for JS parsing and evaluation.
Oh no. While this image covers only the parsing and compilation time of JS (more on this later) rather than overall performance, it is closely related to overall performance and can be used as a metric for overall JS performance.
Quoted Bruce Lawson: "This is the World Wide Web, not the wealthy Western Web." Therefore, your web performance goal is a device about 25 times slower than your MacBook or iPhone. Think about this carefully. But it's even worse. Let's see what our actual goals are.
What is high-performance JS code?
Now that we know the target platform, we can answer the next question: What is High-performanceJS code?
While there is no absolute classification to define high-performance code, we do have a user-centric performance model that can be used as a reference: the RAIL model.
Sam Saccone: Performance Planning: PRPL
If your app responds to user actions within 100 milliseconds, the user will consider the response to be instant. This works for clickable elements, but not for scrolling or dragging.
On a 60Hz display, we want to achieve a constant frame rate of 60 frames per second while animation and scrolling. This means there are about 16 milliseconds per frame. In this 16 millisecond budget, you actually have only 8-10 milliseconds to do all the work, the rest of the time is taken up by the browser's internal mechanisms and other differences.
If you have an expensive and continuously running task, make sure to split it into smaller chunks so that the main thread can respond to user input. You should not have a task that delays user input by more than 50 milliseconds.
You should control the page loading time to less than 1000 milliseconds. After this time, your users will begin to feel impatient. This is a rather difficult goal for the page to be interactive, not just to draw it on the screen and scrollable. In fact, the time is shorter:
Fast By Default: Modern Loading Best Practices (2017 Chrome Dev Summit)
In fact, the goal is 5 seconds of interaction time. This is the metric that Chrome uses in its Lighthouse audit.
Since we know the indicators, let's look at some statistics:
and more information provided by Addy Osmani:
Do you feel depressed enough? very good. Let's get started and fix the web. ✊
Context is crucial
You may have noticed that the main bottleneck is the time it takes to load the website. Specifically, it is the JavaScript download, parsing, compiling and execution time. There is nothing else to do except load less JavaScript code and load more intelligently.
But what about the work your code actually performs besides launching the website? There must be some performance improvements there, right?
Before digging into the code, consider what you are building. Are you building a framework or a VDOM library? Does your code require thousands of operations per second? Are you writing a time-critical library for processing user input and/or animation? If not, you may need to shift your time and energy to a more effective place.
This is not to say that writing high-performance code is not important, but that it usually has little impact on the overall situation, especially when talking about micro-optimization. So before you start arguing about .map versus .forEach versus for loops on Stack Overflow and comparing the results on JSperf.com, make sure you see the forest, not just the trees. On paper, 50k ops/s may sound 50 times better than 1k ops/s, but in most cases it doesn't make any difference.
Analysis, compilation and execution
Fundamentally, the problem with most non-high performance JS is not the running code itself, but all the steps that are required to be performed before the code starts to execute. We are talking about the level of abstraction here. The CPU in your computer runs machine code. Most of the code running on your computer is in a compiled binary format. (Thinking about all Electron applications today, I'm talking about code not
programs.) This means that, except for all OS-level abstractions, it's in you run natively on the hardware without any preparation. JavaScript is not precompiled. It arrives in your browser in the form of readable code, and for all intents and purposes it is the "operating system" of your JS program. The code first needs to be parsed - that is, read and converted into a structure that can be indexed by the computer for compilation. Then compile it into bytecode and finally into machine code before it can be executed by your device/browser.
Another very important thing to mention is that JavaScript is single threaded and runs on the main thread of the browser. This means that only one process can be run at a time. If your DevTools performance timeline is full of yellow peaks that keep your CPU running to 100%, you will run into long/dropping frames, stuttering scrolling, and all sorts of other annoying things.
Paul Lewis: When everything matters, nothing matters! .
So, before your JS starts working, you need to do all this. In Chrome's V8 engine, parsing and compiling account for up to 50% of the total JS execution time.
Two things you should remember from this section:
Although it is not necessarily linear, the JS parsing time is proportional to the packet size. The smaller the amount of JS code, the better.
What you can do, however, is to avoid using JS animation frameworks for everything and understand what triggers drawing and layout. These libraries are only used if regular CSS transitions and animations are absolutely impossible to implement animations.
Even if they may use CSS transitions, synthetic properties, and requestAnimationFrame(), they are still running in JS, on the main thread. They basically just smack your DOM with inline style every 16 milliseconds because there is almost nothing else to do. To keep the animation smooth, you need to make sure that all JS can be executed within 8 milliseconds per frame.
CSS animations and transitions, on the other hand, run outside the main thread—if implemented efficiently, run on the GPU without causing relayout/repaint.
Considering that most animations run during loading or user interaction, this can provide much-needed breathing space for your web application.
Web Animations API is an upcoming feature set that allows you to perform high-performance JS animations outside of the main thread, but for the moment, stick with technologies like CSS transitions and FLIP.
Back size is crucialNow, it's all about packing. Bower and at the end
Dozens of marks before marks The era of marks has passed.Now it's all about installing any shiny new toys you find on NPM, bundling them into a huge 1MB JS file using Webpack and causing slowness in your user's browser while limiting Their data plan.
Try to reduce the amount of JS code. Your project may not require the entire Lodash library. Do you absolutely need to use JS framework? If so, have you considered using other frameworks other than React, such as Preact or HyperHTML, which are less than 1/20 of React? Do you need TweenMax to implement scrolling to top animation? The convenience of isolating components in npm and frameworks brings a drawback: the first way for developers to solve problems becomes adding more JavaScript code. Everything looks like a nail when you have only a hammer. After finishing pruning weeds and reducing the amount of JS code, try to deliver it more
smartly. Deliver what you need when needed.Webpack 3 has amazing
features called code segmentation and dynamic import. Instead of bundling all JS modules into a single app.js package, it can automatically split the code using the import() syntax and load it asynchronously.You also don't need to use frameworks, components, and client routing to get its benefits. Suppose you have a complex piece of code that supports your .mega-widget, which can be located on any number of pages. You just need to write the following in the main JS file:
If your app finds the widget on the page, it will dynamically load the required auxiliary code. Otherwise, everything is normal.
if (document.querySelector('.mega-widget')) { import('./mega-widget'); }
It strips the runtime from all other blocks into its own file, in this case called runtime.js. Just make sure it is loaded before the main JS package. For example:
new webpack.optimize.CommonsChunkPlugin({ name: 'runtime', }),
Then there is the topic of translating the code and polyfill. If you are writing modern (ES6) JavaScript, you may be using Babel to translate it into ES5-compatible code. Translation not only increases file size due to all redundancy, it also increases complexity, and it often reduces performance compared to native ES6 code.
<🎜> <🎜>
The key is that you add nearly 100 kilobytes of data to the JS package, which is not only huge in file size, but also huge in parsing and execution costs, aiming to support older browsers.
However, it makes no sense to punish users who use modern browsers. One method I use, and one that Philip Walton introduced in this post, is to create two separate packages and load them conditionally. Babel uses babel-preset-env to do this easily. For example, you have one package that supports IE 11 and another package that does not contain polyfill for the latest version of modern browsers.
A inefficient but effective way is to put the following in an inline script:
if (document.querySelector('.mega-widget')) { import('./mega-widget'); }
If the browser cannot evaluate the asynchronous function, we assume it is an old browser and just deliver the package containing the polyfill. Otherwise, users will get a simple and modern version.
Conclusion
We hope you will get from this article that JavaScript consumes a lot of performance and should be used with caution.
Ensure you test website performance in low-end devices and real network environments. Your website should load quickly and interact as quickly as possible. This means reducing the amount of JS code and speeding up loading as fast as possible. Your code should always be compressed, split into smaller, easier to manage packages, and load asynchronously where possible. On the server side, make sure HTTP/2 is enabled for faster parallel transfers and use gzip/Brotli compression to significantly reduce JS transfer size.
Speaking of this, I want to end it with the following tweet:
I need to put in a lot of effort to achieve this. But seriously, friends, it's time to abandon your framework and see how fast the browser can be.
— Alex Russell (@slightlylate) September 15, 2016
FAQs about JavaScript Performance Optimization
JavaScript performance issues often stem from inefficient coding practices. These may include excessive DOM operations, memory leaks, and unnecessary calculations. To avoid these problems, it is important to understand how JavaScript works and use efficient coding practices. For example, limit the use of global variables, use event delegates to handle events efficiently, and avoid using with() and eval() because they can cause performance issues.
Loops are common features in JavaScript, but they can also be the source of performance issues if not optimized correctly. One way to optimize a loop is to minimize the work done within the loop. For example, instead of calculating the length of the array in each iteration, calculate it once before the loop starts. Another way is to use the most efficient loop according to your needs. For example, the forEach() method may be slower than the traditional for loop.
Elevation is a mechanism in JavaScript where variables and function declarations are moved to the top of their included scope during the compilation phase. If not understood correctly, this can lead to unexpected behavior and potential performance issues. For example, if a large amount of data is raised but not used immediately, the promotion may result in unnecessary memory usage. Understanding elevation and using let and const instead of var can help alleviate these problems.
When no longer needed memory is not released, a memory leak occurs, causing performance to decline over time. Common causes of memory leaks in JavaScript include forgotten timers or callbacks, separate DOM elements, and global variables. To prevent memory leaks, make sure to clear intervals and timeouts, delete event listeners when they are no longer needed, and avoid global variables when possible.
JavaScript provides a variety of built-in tools for performance analysis and optimization. The Performance API allows you to measure the time spent on different parts of your code and helps you identify bottlenecks. The Memory API helps you track memory usage and identify potential leaks. Additionally, browser developer tools often provide performance analysis capabilities that can help you optimize your code.
Asynchronous programming allows JavaScript to perform non-blocking operations, improving performance by allowing other code to run while waiting for the asynchronous operation to complete. However, incorrect use of asynchronous programming can lead to performance problems. For example, using too many Promise or callbacks can lead to "callback hell" and performance degradation. Learning how to use async/await, promise, and callbacks correctly can help optimize your asynchronous code.
Optimizing JavaScript for mobile devices requires consideration of factors such as limited CPU capabilities and network latency. Techniques for mobile optimization include minimizing the amount of JavaScript code sent over the network, using efficient coding practices to reduce CPU usage, and using progressive enhancements to ensure your website works properly even on low-power devices.
Compression and compression reduce the size of JavaScript files, which speeds up downloads and improves performance. Compression involves deleting unnecessary characters such as spaces and comments, while compression involves encoding data in a more efficient format. Both technologies can significantly improve website loading speeds, especially for users with slower networks.
requestAnimationFrame method allows you to call an animation function before the next repaint of the browser, creating a smooth animation. This can lead to more efficient animations than using setInterval or setTimeout, as it allows the browser to optimize animations based on the current device and loading conditions.
Web Workers allows you to run JavaScript on a separate thread in the background without blocking the main thread. This is useful for performing compute-intensive tasks without slowing down the user interface. However, Web Workers have some limitations and costs, such as the overhead of starting a new worker thread and the ability to operate the DOM directly, so they should be used with caution.
The above is the detailed content of JavaScript Performance Optimization Tips: An Overview. For more information, please follow other related articles on the PHP Chinese website!