The original is on Github, but if you can’t load the images, you can see the original

Making the address

In today’s Internet sites, site response speed is the first element of user experience, its importance is self-evident, here are a few important conditions about response time:

Users will not notice a delay of less than 0.1 seconds when browsing the web;

Delays of less than 1 second will not interrupt the user’s normal thinking, but some delays will be noticed by the user;

If the delay is less than 10 seconds, the user continues to wait for a response.

If the delay time exceeds 10 seconds, the user will give up and start other operations.

So people are starting to focus on performance optimization, and many vendors are starting to do some performance optimization. Some of these are known as Yahoo! Catch-all, but as browsers and protocols have evolved, some of them have become obsolete. So I suggest you look at it in a historical light. For example, minimizing the number of HTTP requests doesn’t work in the HTTP2 protocol, because HTTP2 allows for reuse of HTTP requests, which reduces performance. Therefore, specific optimization principles and means must be viewed in combination with the historical environment, otherwise it will backfire.

Yahoo catch Chinese version: www.cnblogs.com/xianyulaodi…

With the rapid development of mobile Internet, the number of mobile terminals is growing exponentially, and many manufacturers have begun to pay attention to the mobile terminal experience. For example, Google Chrome engineer Alex has come up with the Progressive Web App (PWA) to improve mobile Web performance. The core of PWA is the Service Worker, which enables programmers to control network requests programmatically outside the JS main thread. Combined with the browser’s own cache, it can often achieve a very good user experience. The PWA offers a number of Native like “features” – such as offline loading and desktop shortcuts – to make the mobile Web experience more user-friendly. In addition, the features of the Web itself – such as fast iteration, indexability (which you can search through a search engine, but native apps can’t), etc. – allow more people to invest in the PWA to provide a better user experience for web users. Google came up with AMP much earlier. In 2017, The Google Dev Shanghai site promoted PWA and AMP with A GIF showing them both (PWA’s P and A flipped, then W flipped up and down to make AMP, and vice versa). AMP is a lightweight presentation of the Web for mobile that improves performance by reimplementing heavyweight elements, among other things. Other performance optimizations such as the use of asm.js to make it easier to compile code into machine instructions are also part of the package. If you look closely at the profiles executed by your application, you will find that the COMPILE JS code takes a long time, especially if you write a lot of useless JS code, or code that doesn’t need to be executed in the first place. Js code is eventually compiled into binary code for machine execution, while JS is a dynamic language, which means that JS code is compiled wherever it is executed. This is a big difference from static languages like Java. V8 already optimizes this part quite a bit, and in general we don’t have to worry about it, and in general it’s not a performance bottleneck, because these times are not on the same order of magnitude as network IO times. But in certain situations, compiling ahead of time into code that is easier to interpret and execute may come in handy.

Premature optimization is the root of all evil

When I was new to the front end, I often saw examples of performance optimizations like this:

// bad
for(var i = 0; i < data.length; i++){// do something...
}
// good
for(var i = 0,len = data.length; i < len; i++){// do something...
}

Copy the code

The reason is that the above will calculate data.length every time. Individual optimizations are ridiculous, not to mention how they actually work. Even if the bottom performance is better than the top performance, I think the performance optimization should be left to the compiler, not to the upper business, which loses the understanding, you obviously see that the top performance is easier to understand, right?

Premature optimization, people will fall into a psychological misunderstanding. This psychological myth is the classic hammer in hand, nails everywhere.

Another point is that if you optimize too early, you tend to lose sight of the trees. Performance optimization to comply with the principle of the barrel, that is, the impact of system performance is always the system performance short board. If you optimize too early, you will often have a headache and a doctor’s foot, but there is no effect.

A classical problem

Let’s recall the steps the browser takes from loading the URL to displaying the page:

  1. The browser calls loadUrl to parse the URL
  2. The browser invokes the DNS module. If the browser has a DNS cache, the IP address is returned. Otherwise, query the DNS of the local machine and search for the DNS layer by layer.

Finally, the IP is returned, which is stored in the DNS cache and the expiration time is set.

Tips: In Chrome, enter Chrome :// DNS/to view DNS records cached by Chrome

  1. The browser calls the network module. A TCP connection is established between the network module and the destination IP address.

  2. The browser sends an HTTP request in the following format:

Header (blank line) BodyCopy the code
  1. The request reaches the target machine and establishes a connection to the target Web server through the port.

  2. The Web server obtains the request flow, parses the request flow, and then goes through a series of processing, which may query the database, etc.

Finally, the response flows back to the front end.

  1. The browser downloads the document (content Download) and parses it. The process of parsing is as follows:

Knowing the steps the browser takes to load a web page, we can take a “relatively appropriate” approach to each of these steps to optimize the loading speed. For example, the DNS lookup is performed in the second step above. Therefore, the DNS lookup takes time. If the DNS is resolved and cached in advance, the performance penalty can be reduced. After establishing a TCP connection, for example, the request can be sent sequentially if the connection is maintained. Those familiar with asynchrony know that serialization is costly, and its load time depends on the sum of resource load times. The parallel approach was the longest load time of all. At this point we can consider reducing HTTP requests or using a protocol that supports parallelism (such as HTT2). If you’re familiar with how browsers work or if you look closely at the network load graph, you’ll see that there is a maximum number of resources that can be loaded at the same time (depending on the browser). This is the maximum number of connections that a browser can make with a single domain, so consider adding multiple domains to optimize. There’s a lot more like that, and I’ll leave you to think about it. However, there are only two points summed up. One is load optimization, that is, to improve the speed of resource loading. The second is rendering optimization, which is the optimization between the time the resource is taken and the parsing is complete.

With this brief explanation, I think you’ve got a general idea of how the browser loads the HTML and the page renders, and I’ll talk more about that later. There’s a term called Critical Path, which refers to the process of receiving HTML, CSS, and JavaScript bytes from the browser to the processing necessary to turn them into rendered pixels. There are intermediate steps along the way, and tuning performance is all about understanding what happens in those steps. Remember that the resources on the critical path include HTML, CSS, JavaScript, which does not include images. Although images are very common in our applications, they do not prevent user interaction, so the critical path is not calculated. I will focus on the optimization of images in the following sections.

In order to give you a clearer understanding, I will explain the CSSOM and DOM and Render Tree building process in more detail in step 7 of the browser loading site step above.

The browser requests an HTML file from the server, and the server responds with a byte stream to the browser. The browser receives the HTML and decodes it according to the specified encoding. After the completion, the HTML content will be analyzed, the HTML will be divided into tokens, and then different DOM will be generated according to different tokens. Finally, the DOM tree will be generated according to the hierarchical structure in HTML.

Note that if you encounter a CSS tag or JavaScript tag (not async or defer js script), render will be suspended until the resource is loaded and continue rendering. If the CSS file is loaded (the same goes for the introverted style), the CSSOM is generated after the CSS is loaded. The generation process of CSSOM is similar. CSS is divided into tokens, and THEN CSSOM is generated according to different tokens. CSSOM is used to control the style of DOM. Finally, DOM and CSSOM are synthesized into Render Tree.

CSS is a resource that blocks rendering. It needs to be downloaded to the client as soon as possible to shorten the first render time.

To figure out the exact size and location of each object on the web page, the browser iterates from the root of the render tree, generates a computed style (computed Style in Chrome) based on the box model and CSS computation rules, and finally calls the draw thread to draw the DOM onto the page. Therefore, optimizing each step above is very important. It is now clear that the key resource, HTML, is where everything starts, and without IT there is no meaning. The CSS should be downloaded and parsed as soon as possible. Usually we put the CSS in the head and load it first, just like the concept of the App shell. We should give the user the smallest subset first and then show the rest slowly, just like PJPEG (progressive JPEG). Here is an example of progressive rendering (developers.google.com) :

We haven’t talked about JavaScript yet, which can theoretically manipulate CSS as well as directly modify the DOM. The browser doesn’t know what the JavaScript is, so by default the JavaScript will block the rendering engine and instead execute the JS thread. If it’s an external JavaScript file, the browser must stop and wait for the script to be retrieved from disk, cache, or remote server. This can add tens to thousands of milliseconds of delay to the critical render path, unless you encounter a tag with async or defer. Adding asynchronous keywords to the script tag can significantly improve performance by instructing the browser not to block DOM builds while waiting for the script to become available.

After the above analysis, we know the critical path. With chrome development tools, you can view the waterfall chart, analyze the critical path of your site, and analyze the bottlenecks that slow your site load and affect your site’s speed.

You can also use tools like the aforementioned Web Performance Test, and you can also try Lighthouse.

In the following sections, I’ll introduce the Performance API, which you can bury in the front end and then analyze the performance metrics of your site. This is an important addition to other analysis techniques.

Browser Performance Indicators

Several key indicators

Bad time

The user opens the page until a page begins to render. White screen time is unbearable, so there are many ways to shorten the white screen time. For example, reduce the first screen loading content, first screen content gradually out, etc. The oldest method of measuring the white screen is this:

<head>
<script>
var t = new Date().getTime();
</script>
<link src="">
<link src="">
<link src="">

<script>
tNow = new Date().getTime() - t;
</script>
</head> 
Copy the code

However, this only measures HTML content on the first screen, such as when the react client renders. If client rendering is used, it needs to return on the first screen interface,

And render the page where the dot record.

In a similar way, we can also view the load time of other resources such as images, for example:

<img src="xx" id="test" />
<script>
var startLoad = new Date().getTime()
document.getElementById('test').addEventListener('load'.function(){
var duration = new Date().getTime() - startLoad();
}, false)
</script>

Copy the code

This approach is cumbersome, and the Browser Performance API provides a number of useful interfaces for calculating various performance metrics. The Performance API is covered in detail below.

First screen loading time

By first screen time, we mean the time that the user sees the content rendered and can interact with it without scrolling. As for load time, it’s the time it takes for the entire page to scroll to the bottom and for all content to be loaded and interacted with. Users can perform normal event input interaction operations.


firstscreenready - navigationStart

Copy the code

This time is the user’s actual perception of the speed of the site. There is no performance API for FirstScreenReady, and different rendering methods (server rendering and client rendering are calculated differently) do not make the same generalizations. The specific calculation scheme is quite detailed in this article. First screen time calculation

Full loading time

Usually web pages to trigger the time of two events to determine the page load time.

  1. The DOMContentLoaded event represents the content directly written in the HTML page, but does not include the time when external resources (CSS, JS, images, flash, etc.) are loaded. External resources refer to the content that needs to generate additional HTTP requests.

  2. The onLOAD event, which indicates the time when the load was completed along with the external resource.


domComplete - domLoading

Copy the code

Performance API

The above explains the ancient method of measuring key metrics, based on the principle of loading the browser from top to bottom. Only the above method is more troublesome, not suitable for use in the actual project. In the actual project or use the way of dot. The Performance API is a way of burying points in key places and then calculating them as needed to get the metrics we want to see.

The Performance interface provides access to performance-related information for the current page. It’s part of the High Resolution Time API, but is enhanced by the Performance Timeline API, the Navigation Timing API, the User Timing API, And the Resource Timing api. —— from MDN

Enter performance. Timing in the browser console

Each byte returned corresponds to each state of the following Performance process and returns the time. This is not the same as new Date().gettime (). This time has nothing to do with real time, and the Perfermance API is more accurate.

With this Performance API, we can easily calculate performance metrics. If the performamce API is not buried enough for us, we can also customize some of the metrics we care about, such as request time (counting success and failure separately), long JS operation time, or important features. In short, anything we want to count, we can easily do with the Performance API.

For more information about the Performance API, visit developer.mozilla.org/en-US/docs/…

Means of performance monitoring

The log

Monitoring is log based

A well-formed, comprehensive log is an important condition to achieve monitoring, it can be said that the foundation determines the superstructure. A good logging system usually has the following components:

  1. Access layer

The process from log generation to log entry. For example, logs generated by rSyslog are accessed to the log system through logstash (Transport and Process your logs, events, or Other Data). This is relatively simple and, since it is based on a native Linux logging system, relatively inexpensive to learn and use. If different systems of the company need to intervene in the log system, you only need to write the logs to the log directory and collect them through logstash.

  1. Log processing layer

This part is the core of the log system. The processing layer can analyze the logs generated by the access layer. Send filtering logs to the monitoring center (monitoring system status) and storage center (data summary, query, etc.)

  1. Log storage layer

Logs are stored in the database and indexes are created based on service conditions. This part can also be accessed by a library such as Elastic Search, which provides log queries. Logstash is part of the Elastic family.

In addition to the core layers above, there are often other layers that do more detailed work.

Generally speaking, a company’s journal has several aspects

  1. Performance logs

Record some key metrics, which you can see in the Browser Performance Metrics section.

  1. The error log

Record server errors (500,502, etc.) on the back end and script errors on the front end.

  1. Hardware Resource Logs

Record the usage of hardware resources, such as memory, network bandwidth, and hard disk.

  1. Business log

Records the operations of users concerned by the business side. Locate faults according to the exceptions reported by users.

  1. Statistical log

Statistics logs are usually collected based on the contents of stored logs. Statistical logs are a bit like database views, which shield the structural information of the database and expose a part of the database to the user. User behavior analysis is usually based on statistical log analysis. Some companies are even involved in visual log statistics systems (such as Kibana). With the rise of AI, AI + logging is one direction.

Performance logs

Output of performance logs

In addition to the key indicator records described above. We are also usually concerned with the response speed of the interface. At this point we can record by way of dot.

Consumption of performance logs

We have generated the log, we have the log data source. So how do you consume data? Nowadays, it is common for the server to dump the collected logs and display them to the administrator by visual means (such as ICONS). There is also a user’s own consumption, namely self-production and self-marketing. Users generate data and consume it themselves, providing a much better user experience. The details of the locus but the performance log obviously can not be produced and sold, we consider only the first one for the time being.

Performance monitoring platform

Large companies have their own systems for monitoring platforms. Like Hawk, like Ali’s SunFire. Small companies often use open source monitoring systems or none at all. My previous company did not have any monitoring platform, at most it was only the monitoring data provided by Ali Cloud. So I did some exploration in this area. And began to develop rosefinch platform, but due to the limited energy, the plan was not finally put into use, which is quite a pity. The essence of performance monitoring is to provide easy query and visual statistics based on monitored data. A warning is issued when a threshold (and often a duration limit) is exceeded. The previous section introduced the performance monitoring platform and mentioned the two components of the performance monitoring platform, a producer and a consumer. This section describes how to set up a monitoring platform. So let me take a look at the overall architecture

For the sake of explanation, only a simplified model is implemented here, and the reader can further divide subsystems such as SSO access, storage and presentation separation, etc.

The client

On the one hand, the client reports the buried point information and on the other hand, the trajectory information.

Report the burial site

This part mainly uses some means, such as the Performance API to report the page related load time information to the back end.

performance.getEntriesByType("resource").forEach(function(r) {
    console.log(r.name + ":" + r.duration)
})

Copy the code

On the other hand, the specific asynchronous request interface, dot. Dot all user interactions (clicks, hover, etc.)


const startTime = new Date().getTime();

fetch(url)
.then(res= > {
   const endTime = new Date().getTime();
   report(url , 'success', endTime - startTime);
})
.catch(err= > {
	const endTime = new Date().getTime();
	report(url, 'failure', endTime - startTime);
})

Copy the code
Report trajectory information

Uploading track information is easy. If it is the page granularity, it can be reported directly on the page. If a front-end route is used, it can also be reported in the hook function of the route.

pageA.js

// Report the trajectory
report('pageA', {userId: '876521'.meta: {}})

Copy the code

So we have a data source.

The service side

The server already has the data, and the back end needs to format the data and output it.

locus server

The client reports its information to the server. The server collects statistics and sends the processed data to the client to guide the client’s behavior (such as preloading).

The information uploaded by the client is approximately

{
 userId: 876521.page: "index".area: "".age: "".// Other group characteristics
}
Copy the code

We call the region, age and so on group characteristics, group characteristics for statistical analysis of very important significance.

We can aggregate individual users and we can aggregate group characteristics to predict customer behavior.

Our aggregated data may be:

{
  userId: '876521'.pages: {
  	"index": {
  		"detail": 0.5."return": 0.4."personnal-center": 0.1}}}Copy the code

The above analysis takes the user as the latitude. The above data indicates that if user 876521 is on the index page, the probability of his/her next visit to the detail page is 50%, the probability of his/her personal center page is 10%, and the probability of his/her exit page is 40%.

If possible, we can preload the details page while the user is still on the home page.

We can also aggregate group characteristics,

The result may be:

{
  age: 'teen'.pages: {
  	"index": {
  		"detail": 0.5."return": 0.4."personnal-center": 0.1}}}Copy the code

It’s similar to the above, but it’s not just for a specific user, it’s for the same demographic characteristics (in this case, the same age group).

zhuque server

The client uploads performance information to the ZHUque Server.

Zhuque Server has two main responsibilities here:

  1. Visual output of user-reported data for different people to view.

This part is usually easier to do than to do well. I don’t have enough experience in this area to mislead.

  1. Provide alert services, such as pages are not responsive for a long time, cannot open, critical resource 404 and other issues.

There are many kinds of alerts, such as email, phone, text, nail and so on. We just set the trigger condition, write a scheduled task or check at the request level, and fire an alert if it’s met. The logic is very simple.

Scheduled tasks put less pressure on the system but have low timeliness. Therefore, scheduled tasks are suitable for services that do not have strong real-time requirements. Request level check 𫍷 The system pressure is large, but the timeliness is guaranteed, suitable for the very high requirement of real-time

Means of performance optimization

To optimize performance, we must first have a complete understanding of the process of system operation, and then analyze from each link to find the bottleneck of the system, so as to optimize. Here I will not list the various means of performance optimization, but from the front three aspects of the common optimization direction and means of performance optimization. If you want to have a complete optimization list, here is a complete front-end-checklist, which can be used as a reference for performance optimization. You can also visit WebPagetest to test the performance of your site and take steps to optimize the loading speed of your site based on the feedback your site provides, which is beyond the scope of this article. One of the most important principles of performance optimization is to always present what is necessary. We can either lazily load non-first-screen resources or “load on demand” data through paging. Here are some specific optimization methods. As many of you know, the front end divides the application into three layers: the structural layer, the presentation layer, and the behavior layer. Let’s talk about the direction of performance optimization from three levels.

The structure layer

The structure layer refers to the DOM structure, and the DOM structure is usually determined by the HTML structure, so we mainly analyze the performance optimization points of THE HTML structure. We know that DOM manipulation is very expensive, as mentioned earlier in the history of the front end. How to reduce the number of DOM, reducing DOM operations, is where optimization needs to focus.

AMP HTML

Speaking of HTML optimization, I have to mention AMP HTML. The core idea of AMP is to provide a better user experience on mobile. It consists of three core parts: AMP HTML, AMP JS and AMP Cache.

AMP HTML is HTML with some restrictions for reliable performance.

Here is typical AMP HTML

<! doctypehtml>
<html⚡ >
 <head>
   <meta charset="utf-8">
   <link rel="canonical" href="hello-world.html">
   <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
   <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1normal both; -moz-animation:-amp-start 8s steps(1,end) 0s 1normal both; -ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none; -moz-animation:none; -ms-animation:none;animation:none}</style></noscript>
   <script async src="https://cdn.ampproject.org/v0.js"></script>
 </head>
 <body>Hello World!</body>
</html>
Copy the code

As you can see, AMP HTML consists of regular HTML tags and AMP tags. What does AMP label do? Let me tell you, DOM is expensive to operate, but different DOM efficiencies are meaningless. For example, rendering an A tag is definitely not the same as rendering an IMG or table tag. We call the elements that the a tag renders faster in this way light elements. Elements such as table and img are heavy elements. We should try to avoid heavy elements, such as table can be implemented using UL li. Img is slow because of images are downloaded is asynchronous, but thread take up network, as well as multiple a request (browser concurrent requests is limited), so it can further encapsulated said lighter elements (such as X-ray image, the component internal request to delay sending pictures, waiting for the main structure rendering after again hair pictures request). Consider wrapping it as a Web-Component or another component (such as a React component)

Going back to the AMP HTML, there is an interface in AMP like amp-image, which you can probably implement on your own. The x-image is actually the component that implements the AMP interface specification.

Reduce unnecessary nesting

I talked about using light elements whenever possible. So in addition to using light elements, it’s also important to reduce the DOM. An important way to reduce the number of DOM is to reduce redundant tags. Let’s say we clear the float by adding a new element.

<div class="clear"></div>
Copy the code

But for now, it’s all pseudo elements. Another approach is to reduce the level of nesting. Many people wrap

or

    tags around

    tags. Why add a

    tag that you don’t need? You can actually do the same thing with CSS Selector. The point is, I’ve seen this kind of code a lot.

<div class="form">
	<form>.</form>
</div>
Copy the code

It could have been written like this:

<form class="form">.</form>

Copy the code

The presentation layer

The presentation layer is what we usually use as CSS. CSS after the browser parsing will generate A CSS TREE, and then DOM TREE synthesis of RENDER TREE. Sometimes some functions can be implemented with CSS without javaScript. Especially for animations, CSS3 adds Transition and Transform for animation, and developers can even get the most out of the GPU with 3D acceleration. Therefore, skilled use of CSS, and grasp the CSS optimization skills are essential. CSS performance optimization usually focuses on two aspects:

Improves the loading performance of the CSS

Improving load performance means reducing the time it takes to load. In short, reduce the size of CSS files, speed up page loading, and use HTTP caching whenever possible. At the code level we should avoid introducing unnecessary styles and use inheritance to reduce code.

<body>
	<div class="list" />
</body>

Copy the code
body { color: # 999 }
.list {color: # 999} / /colorYou can actually inheritbodyTherefore, this line is not necessaryCopy the code

Other attributes that can be inherited are color, font-size, and font-family.

Generally this is the same as for static resources such as JS. At the moment, however, there is a concept of frameworkfirst, that is, loading the theme framework of the site first, which is usually the static part, and then loading the data dynamically, which gives the user the impression that the site is “fast”. The static content in this section is usually done with a simple HTML structure (Nav + footer) and a CSS style. This requires the CSS of the theme framework to be loaded first. We can write this part of the framework style into the introverted style, but some people think this is not good for code maintenance.

Improve CSS code performance

Browsers execute different code differently, and complex styles (multiple layers of nesting) can also reduce CSS parsing efficiency, so complex nested styles can be transformed.

.wrapper .list .item .success{} // Can be written as:.wrapper .list .item-success {}
Copy the code

The other part is the animation of the website, which is usually less than 16ms to make the user feel very smooth. In addition, we can make full use of GPU performance through 3D acceleration. Here is a quote from Jiang Shui:

It is only in very complex pages with a lot of styles that the performance bottleneck of CSS will be highlighted, and the more important consideration should be whether it is necessary to make such a complex page.

Behavior layer

The behavior layer refers to the user interaction aspect of the content, which is mainly implemented in JavaScript on the front end. The current JavaScript specification has reached ES2017. The most impressive front end is ES6 (also known as ES2015), because ES5 was released in 2009, and it took six years for ES6 to officially launch in 2015, adding a lot of exciting new features that are familiar to the broad front end. At one point, it even called the current state of the front end 536 (HTML5, CSS3, ES6), which shows its influence.

It’s no exaggeration to say that the vast majority of the code in front-end projects today is javascript. Since js is used so much, why is there so little talk about JS performance optimization? First, due to the development of industrial technology and the performance improvement of hardware devices, front-end computing performance is generally not considered as a performance bottleneck of a system. Second, with the release of V8 engine, JS execution speed has been greatly improved. Third, because computing performance is the work of local CPU and memory, it is not an order of magnitude relative to network IO, so people focus more on IO optimization. So why optimize JS performance? For one thing, the front end currently does some middle tier and even back end through Node, so you need to focus on memory usage, which is very different from the browser. On the other hand, because the front end sometimes writes a complex calculation, there are performance issues as well. The last point is whether we can optimize the performance of network IO through JS, such as using JS API to operate webWorker or using localStorage cache.

Calculate the cache

Occasionally there will be some big calculations on the front end. For complex operations, we usually cache the results for the next time. The concept of a pure function was mentioned earlier. To use a computational cache, the function must be pure. A simple cache function code looks like this:


/ / define
function memoize(func) {
  var cache = {};
  var slice = Array.prototype.slice;

  return function() {
    var args = slice.call(arguments);

    if (args in cache)
      return cache[args];
    else
      return (cache[args] = func.apply(this, args)); }}/ / use
function cal() {}
const memoizeCal = memoize(cal);

memoizeCal(1) // Calculate and cache the results
memoizeCal(1) // Return directly

Copy the code

Network IO Cache

Computational optimization, which is relatively small in scope. Because not all systems have complex calculations. But network IO exists in all systems, and network IO is unstable. Network IO is an order of magnitude faster than local computing, but our browser handles network requests asynchronously (and, of course, can be coded to do so synchronously). One way is to use local caching to store the results of network requests locally and read them directly on the next request without having to send the request repeatedly. A simple way to do this is:

function cachedFetch(url, options) {
  const cache = {};
  if (cache[url]) return cache[url];
  else {
   return fetch(url, options).then(res) {
      cache[url] = res
      returnres; }}}Copy the code

Of course there are many problems with the rough implementation above, such as the absence of a cache invalidation policy (such as the LRU policy or via TTL), but the basic idea is this. The advantages of this approach are obvious, as is the significant reduction in system feedback time, but the disadvantages are equally obvious. Due to the use of caching, when data is updated, it is necessary to consider the issue of cache update synchronization, otherwise the data will be inconsistent, resulting in a bad user experience.

Data structure, algorithm optimization

Data structure and algorithm optimization is less front-end contact. However, if there is a large amount of computation, in addition to the use of cache, but also with some data structure optimization and algorithm optimization. Let’s say we have 50,000 orders.

const orders = [{name: 'john'.price: 20}, {name: 'john'.price: 10},... ]Copy the code

I need to frequently look up the order information for one of them on a given day. We can take the following data structure:

 const mapper = {
   'john|2015-09-12': []}Copy the code

So the speed at which we can find information about someone’s order on a given day will be O(1), which is constant time. You can think of it as an index, because an index is a data structure, so we can also use other data structures and algorithms for our unique items. For algorithm optimization, we first need to be able to identify the complexity, the common complexity is O(n) O(logn) O(nlogn) O(n2). On the front end, it’s basic to recognize code with bad complexity, such as n to the third or n factorial code. We don’t need to write code that performs very well, but we should try not to write code that is too complex.

Multithreaded computing

Webworker, a new HTML5 API, allows developers to transfer computation to worker processes and then send the computation results back to the main process through process communication. There is no doubt that this approach has a significant advantage in requiring a lot of computation.

Code from Google Performance:

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message'.function(evt) {
   var sortedData = evt.data;
   // Update data on screen...
});
Copy the code

Because WebWorker is so restricted that it cannot access objects such as Windows and documents, you will have to find other methods if you need to use them.

One way to use a Web worker is to divide and conquer, to divide a large task into several smaller tasks, and then summarize the results. We usually use an array data structure to accomplish this. Here is an example:

// An array of small tasks
var taskList = breakBigTaskIntoMicroTasks(monsterTaskList);
// Use the updated API requestAnimationFrame instead of setTimeout to improve performance
requestAnimationFrame(processTaskList);

function processTaskList(taskStartTime) {
  var taskFinishTime;

  do {
    // Assume the next task is pushed onto a stack.
    var nextTask = taskList.pop();

    // Process nextTask.
    processTask(nextTask);

    // Go again if there’s enough time to do the next task.
    taskFinishTime = window.performance.now();
  } while (taskFinishTime - taskStartTime < 3);

  if (taskList.length > 0)
    requestAnimationFrame(processTaskList);

}
Copy the code

Thread safety issues are caused by both global and static variables. In general, global variables are thread-safe if each thread has only read and no write operations on them. When multiple threads are writing simultaneously, thread synchronization needs to be considered, which can lead to thread-safety issues.

You don’t have to worry too much, web Worker has done a lot of work in this area, such as you don’t have access to non-thread-safe components or DOM, and you need to serialize objects to interact with thread specific data. So if you want to write thread-unsafe code, it’s not that easy.

Code optimization under V8 engine

V8, proposed by Google, improves performance by compiling JS code into machine code rather than tuple code or interpreting it. V8 also packs a lot of efficient algorithms inside, and many developers will study the SOURCE code to improve themselves. There are some js optimization practices that you can see in this article and many other interesting studies.

Benedikt Meurer(Tech Lead of JavaScript Execution Optimization in Chrome/V8) has written a number of in-depth articles on V8 performance and open source a number of interesting projects. Those who are interested can pay attention.

A memory leak

Memory leaks in the front end are not very common, but it’s worth knowing about them, at least to fix them if they do. More low-level languages, such as C, have memory malloc and memory free. On the other hand, high level languages such as Java and JS mask the details of memory allocation and destruction, and then use the garbage collector (GC) to clear the memory that is not needed.

Only the developer knows when to destroy memory.

Fortunately, there are some rules to follow in memory destruction. At present, there are mainly two GC garbage collection strategies, one is reference counting, the other is unreachable detection. Both algorithms are currently implemented in major browsers, and both algorithms are used to optimize memory. There are, however, points that the above algorithm cannot cover, such as closures. So it’s still up to the developer’s awareness, so it’s useful to know how memory leaks work and how to fix them. The following are a few cases where memory leaks can easily occur.

The tail call

Function calls have some overhead, in the sense that they need to allocate stack space for the function. If called recursively, it may cause a stack burst.

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}
factorial(100) // Everything is ok
factorial(1000) // It is possible to explode the stack, but now browsers are optimized to print Infinite
}
Copy the code

It gets worse if you use more complicated operations here, and it gets worse if you add closures.

closure

Since JS has no private property, if JS wants to implement the function of private property, it is necessary to use closure to achieve.

 function closure() {
   var privateKey = 1;
   return function() {
     return privateKey
   }
 }

Copy the code

However, due to the garbage collection mechanism of JS, JS will release the memory without reference periodically. If the closure is used, the function will keep the reference of the variable, so that it cannot be destroyed within the garbage collection cycle. If the closure is abused, memory leakage may occur.

Image optimization

The above three layers from the front end of the analysis of the means of performance optimization, but there is one, and is a very large proportion of resources not mentioned, that is the picture. As the saying goes, a picture is worth a thousand words, pictures in the current site occupied most of the traffic in the site. While images do not block user interaction and do not affect the critical path, the speed at which images are loaded is important to the user experience. Especially mobile Internet is so developed today, it is very important to save traffic for users. So there are two main picture optimization, one is to use the picture when necessary, when unnecessary to use other ways. The other is to compress the size of the image.

Make it clear if you need pictures

Start by asking yourself if you really need an image to achieve the desired effect. Good design should be simple and always provide optimal performance. This optimization strategy is always the best if you can eliminate image resources (which typically require a larger number of bytes than HTML, CSS, JavaScript, and other resources on the web page). However, images can also tell more than a thousand words if used correctly, so it’s up to you to find the balance.

Compressed image volume

First, let’s look at what determines the size of an image. You might want to know a little bit about graphics here. Pictures are divided into bitmaps and vector maps. A bitmap is a representation of pixels in bits, and then the pixels make up a picture. One concept of bitmaps is bit depth, which refers to the number of bits used to store each pixel. So there is a formula for calculating the size of a bitmap: the number of pixels * the number of deep bits. Note that the units are bits, which can also be converted to KB or MB for easy viewing.

Picture pixels = picture horizontal pixels * picture vertical pixels

The vector graph is composed of mathematical vectors, file capacity is small, in the operation of magnification, reduction or rotation of the image will not be distorted, the disadvantage is not easy to make color changes too much of the image. So the vector graph is the computer through the data calculation, so it takes up little space. Usually vector map and bitmap will also be converted to each other, such as vector map to print will be bitmap.

The following picture optimization refers to bitmaps. Knowing what determines the size of an image, the way to reduce the size of an image is to reduce the resolution or use a lower bit depth image format.

Reduced resolution

When we are developing, the designer will give us 1x2x3X images, which have different pixel counts. 2x has four pixels as many as 1x, 2×2, and 3x as many as 3×3, 9 pixels. The picture is directly 9 times larger. Therefore, when using images on the front end, it is best not to use 3x images directly and then tile them across different devices. This will require the browser to rescale them (which will also use additional CPU resources) and display them at a lower resolution, thereby reducing performance. The following table data comes from Google Developers

Note that in all of the above cases, the display size is only “10 CSS pixels smaller” than the resources required for each screen resolution. However, the number of extra pixels and their associated overhead increases rapidly with the size of the image display! So, while you may not be able to guarantee that every resource is provided at an exact display size, you should ensure that the number of excess pixels is minimal, and that particularly large resources are provided at a size as close to their display size as possible.

We can use media queries or srcsets to load different resources for different screens. But nine times that size is still hard to accept. Hence the following approach.

Reduce the depth

Bit depth is the number of bytes used to represent a color. Bit depth is 24 bits, which means that 256 (2 to the power of 24/3) bits are used to represent a color. So the deeper the bit, the finer the picture. If possible, reducing the bit depth will reduce the volume.

The compression

The picture size = the number of pixels * bit depth, in fact, more strictly is the picture size = the number of pixels * bit depth * picture quality, so the lower the picture quality (Q), the smaller the picture will be. There are many factors that affect the quality of picture compression, such as the number of color types of the picture, the number of adjacent pixels with the same color and so on. There are many image compression algorithms, the current more popular image compression is webP format. So use webP whenever you can.

The concrete practice of picture optimization

With the above theory in mind, we need to put the theory into practice. If you load the original image and use CSS to control the display, you will find that you will load a very large image, whereas it should be very small. So it would be nice if we could control to only download what we want to display in thumbnail form. We hope that we can be the size of the specified image by means of https://test.imgix.net/some_file?w=395&h=96&crop=faces, thereby reducing the waste of bytes. There are already photo services that offer this functionality. Such as imgix.

One of the advantages of Imgix is the ability to find interesting areas in an image and crop them. Instead of just cropping out the center of the image

The webP mentioned above can also be supported by the CDN vendor, that is, when we upload pictures, the CDN vendor will store a copy of the WebP. For example, we upload a PNG image, https://img.alicdn.com/test/TB1XFdma5qAXuNjy1XdXXaYcVXa-29-32.png. We can then its corresponding webp resources access by https://img.alicdn.com/test/TB1XFdma5qAXuNjy1XdXXaYcVXa-29-32.webp. We can then load webP or PNG images depending on the browser’s support.

The second effective way is lazy loading. An important idea is to load only the images that should be displayed at this time. If you’re using React, you can use react-lazyload to lazily load images. Other frameworks can search on their own.

import LazyLoad from 'react-lazyload';


<LazyLoad once height={200} offset={50}>
  <img
    srcSet={xxx}
    sizes={xxxxx}
  />
</LazyLoad>

Copy the code

conclusion

What if you’ve put in a lot of optimizations, but the user still feels it’s too slow? You know, performance isn’t measured, it’s intuitive, like I said at the beginning. One way to make animations feel faster at the same speed is to use them properly. Like a progress bar with 90% of your current progress, a running bear? But be careful, because the unreasonable animation design, but let the user disgusted, just imagine, when you see a long-awaited confirm button, but it is a running bear behind the block, the essential point, what will be your heart?

I don’t cover all the points of performance optimization. For example, Google’s ProtoBuffer can improve performance by reducing the amount of data transmitted in front and back ends. But with the above optimization theory and ideas, I believe that these things can be seen and done

Welcome to follow my public number < imagination front end >, not regular updates.