📜 ⬆️ ⬇️

Lecture of Vitaly Kharisov "10k"

Vitaly vithar Kharisov is one of the key developers and managers of Yandex. At the Moscow Ya. Subbotnik on the frontend, Vitaly told about an easy version of the search for slow connections and ways to optimize the code, allowing it to fit into 10 kilobytes.


- My name is Vitaly Kharisov, today I will read the report under the strange name 10k. The title of the report refers to the competition held by Microsoft last August - 10k Part. This is a contest to make a full-featured site that fits perfectly into 10 kilobytes, it's all its HTML, CSS, JS, images — everything we request over the network. Under the terms of the competition, it was possible to add additional resources that do not affect the main functionality of the site, with separate requests. But the site itself had to be fully functional and fully accessible, operational within 10 kilobytes.

Around August of the same year, we began working on a project codenamed Mobile Granny. We have a project called “Granny” for quite a while. This is the Yandex issue project for very old browsers - IE6, 7, 8, Firefox 3.6 and so on. It offers browsers adapted for them layout. And we send all the old browsers to Granny to make it easier for us to develop a basic version of the search.
')
When choosing a name for the light version of Yandex, which will be shown on slow connections, the name “Mobile granny” was automatically selected.

It looks like this. Visually, you will not find differences between a full and light version of Yandex. The differences are that we show the light version when the user has a slow connection. You go to the subway, fell into the EDGE, went to the cottage, fell into the EDGE - in the end, you can have a modern phone, no Internet, and you load the issue for a very long time. You need to do something about it and show you some version easier.

According to our statistics, such requests we have about 60 million hits per month. About 8% of mobile users in issuing Yandex are dumped into the light version.

It should look exactly the same as the main search. It must be calculated by exactly the same counters. It for statistics should look the same.

And we made a restriction ourselves: the light version of Yandex, the HTML that we do, should come to the user for 10 TCP packets, about 14 Kb. This is the maximum limit set for the light version.

Light version in production from about February, it can be used, there is traffic, 8% of users fall on it. If you want to watch it on your laptop or smartphone, you can do this by adding a lite parameter or by following the short link bit.ly/10k-2016, where these parameters are already sewn.

What did we want to do? Light version should load quickly. It should load and display quickly. To do this, we made the minimum number of HTTP requests - so that it is delivered to the user as soon as possible. We will definitely use gzip for this, now it is the minimum set that should be included on any site. And if for some reason your site still does not use gzip compression, turn it on. You will receive a profit for the download automatically.

In order to simultaneously speed up loading, and speed up drawing, we include the styles and scripts of the main version of the page inside the page. First of all, it saves us from unnecessary TCP-requests that work poorly on a slow connection, and at the same time we load what we need to show the user exactly at the moment when it is necessary.

At the same time, the page we downloaded to the user should work as soon as it is displayed. And we can add additional functionality with separate requests. We do this in the form of a single JS file, where we include a sadzhest, some performance counters, sending error handling, setting cookies, additional things, without which, in principle, page functionality does not work. We are loading it with a separate bundle. And we load the metric with a separate query. If suddenly on a slow connection the metric does not reach the user, we just do not count the statistics, but at the same time the user can use the Yandex output.

An additional downloadable resource is search.js - we make its gzip version, this is the minimum set. We use zopfli for this, because it presses as good as possible, and we use brotli, because the compression version is better, it gives about 10% profit compared to gzip.

And in fact, Can I use tells us that now it can be used in 50% of browsers. There is no reason not to use a better compression algorithm.

Before you start talking about optimization, it would be nice to see the actions that you do on your page. The code that you are optimizing - you should see that you got a profit after gzip. There are cases that you make changes, make gzip and see that your page has not decreased from these changes, but on the contrary, has grown. We need to have instruments that show that everything we do benefits us.

The simplest option: we have the previous and next version of the page, we derive their size, we understand that our new one has become smaller than the old one. So we did something right, we have a profit.

In the case of issuing Yandex, this does not work. Our page is dynamic, very variable, it depends a lot on what the user entered and what came from the backend as an issue. Therefore, we have a separate internal service Pulse, we run it for each pull-request, this is one of the checks. We have about a dozen checks, and Pulse is one of them. It shows how much the patterning time has changed, how much the size has changed before and after gzip, and we can see that this pull request does not bring any bad changes. In the screenshot, we see that the templating time has slowed down, while the HTML size has decreased. We did this optimization, which extended the templating to us, and the resulting HTML became smaller.

And we can watch the dynamics in Pulse-Requests in Pulse - which, for example, we dramatically improved everything in some pull-request. Or that in some pool of the request you can find ends where we broke something and worsened something.

About optimization. The first point is the optimization of loading, rendering.

The architecture of our output is designed so that we have two stages of drawing and transferring data to the user. We have a so-called presearch. As soon as the user makes a request to search for Yandex, we can immediately reply to the user. And even without making requests to backends, without even starting to look for what the user asked us and how to answer him, - even before all of the above, we can already display the header, output the user's request, which can then be corrected. At the same time - start looking. This issue will already go to the user in his browser. And draw everything else, already when we processed the request.

When developing a light version of the search, it was decided that we display the styles inside the page. And we do not draw styles in the usual manner, in the header, but in in-place styles in the response body at the exact moment when these styles are needed. If the basement is at the very end of the page, the styles for the basement will arrive at the very end of the page - exactly when the user loads up to them and when they start to be used. If he has a very slow connection, the connection is cut off and he doesn’t load the page, the extra Baitics that he didn’t need at that time would not be loaded to it.

At first, we specifically wrote such styles in the template engine, inserted them by hand. Then they thought a little and wrote an automatic mechanism. We decompose the entire BEM file structure and can collect all styles on a project into some JSON at the time of assembly. Since we use the BEM template engine and can declaratively fill it with templates separately, from the side, we wrote a small template. Before displaying any house in any HTML, it looks to see if we have selected more styles for it. If we didn’t draw styles, we add them, and if we draw, we don’t do anything. This allows us to automatically display styles in HTML at the exact moment when they first came in handy. And we do not write anything separately in the code for this. We have written a separate template that does this, and we do nothing more.

The thought went on. We have not implemented it, we will implement it in the near future.

We also thought that every time we transmit these styles over the network, they are the same. If the user makes several requests in succession, then we will transfer the styles to HTML in place in HTML each time. We thought and decided that we would implement the following mechanism: when we loaded the page to the end, we could go with a separate request, load the CSS that is needed for the whole of this page. Fortunately, in the light version of Yandex, it turns out about 15 kilobytes. To add to the cache, set the cookie that we have already loaded it - and when you re-request, instead of inserting styles in HTML in place, we make the usual query for styles, which raises them from the cache. Then we can not display them in HTML. This has not yet been done, but not so difficult to do.

Understood with loading, with drawing. Next, let's go through the technologies: where are the angles we can cut to get an easier and more optimal version.

First of all, in HTML there is a default attribute value. We have gzip, it compresses everything well, but the best compression is if you don’t compress anything. And if we can not transmit something over the network, but use it by default, then we will do it. We have HTML 5, where script and style have default attribute values. We simply do not display them.

The next place to save money is to use Entity.

Entity was invented at the dawn of the Internet, when there was an ASCII table and nothing more. We could not in any way insert special characters into the HTML code, and such mnemonic substitutions were invented. Now there is already UTF-8 everywhere, and we can use symbols directly instead of substitutions.

As a rule, characters weigh a little less.

A typical piece with which everyone starts writing their HTML. Not many people know that we can specify neither html, nor head, nor body. We don’t need them at all.

And this is completely valid HTML, which works just like the previous version. Just weighs less.

Not everyone knows that some of the closing tags can be omitted from HTML.

Parser they are automatically understood from the context. They do not make sense to transmit.

Such tags are not so small. Also there is something to save.

My favorite. Attributes are quoted. In some cases, we can omit these quotes if, for example, we have no attributes in the body of gaps. If there are no special characters, we can transmit such a code.

He is also absolutely valid and works.

Since we do not write HTML with our hands, but use bem-xjst to get such optimal HTML, we just need to enable the option not to output the final tags, not to display quotes. And the result is optimal HTML.

With HTML finished. What can we do with CSS?

The best we can do about CSS is to use the optimizer. There is CSSO, the best CSS structural optimizer on the market, which is fast, tight, doing everything perfectly. Use it. In any case, whether you are developing a light version or a hard version, whether you are inserting multi-megabyte videos into your HTML - just use CSSO always, it will do well.

In addition to it, if you have a media query, I recommend using such a plugin for post CSS that can combine these media query. CSSO does not know how, it has tissue, a separate plugin that allows you to combine media query equally and get a little more optimal code.

And do not write in CSS those things that you can not write. Suppose a rule on a slide is equivalent to what is crossed out. If you can not write any letters - just do not write.

Everything is simple with CSS and HTML. With pictures of work a little more.

The best thing you can do in your project is that it weighs less and loads faster - if possible, use vector graphics. Use SVG, not binary images. As a rule, SVG weighs less. He also sticks separately in gzip, and still it can be beautifully inserted into CSS.

For SVG use SVGO. It has a bunch of plugins that allow you to modify the SVG in one way and make it as optimal as possible. At the same time, the functionality is exactly the same, it just weighs less. And for binary images, use ImageOptim for Mac. If you have another OS, there are a lot of different analogues that can iterate over image compression algorithms and give you the optimal picture of the minimum format.

Here you can save a couple of dozen baytik. Sometimes GIF weighs less than PNG, surprisingly enough. On very small pictures, on some pictograms, on monochrome pictures GIF weighs less. If in your project every byte is expensive, you can try to save to PNG and GIF and choose the smallest one.

About SVG. It can be inserted into the code in different ways. If you want to change the SVG, change its properties, then there are no other options than to embed the SVG directly into HTML. In our case, this is not necessary: ​​we use SVG just as pictures. Then we want to use the "Granny" and for very old smartphones that are completely stupid. And there are more phones that do not support SVG. They are used by people, they are quite a lot. We need to determine whether the browser supports the user SVG, and give the user either SVG or a binary image. Such a code snippet allows us in the browser to determine whether we support SVG, and put the corresponding class on the root element of the document - is there SVG or not.

After that, we can start writing about the following CSS code. SVG is - use. No - use pictures. Then we pass it through the collector when we collect the production version.

We insert SVG-pictures directly into CSS, and download PNG-pictures over the network, because with a very high probability, if our browser does not support SVG, it also doesn’t support data: URI base64.

I want to note that the SVG image is inserted as data: URI, but it does not use base64, but URIencoded. In this case, compared to base64, the size of the image is much smaller, and the SVG inserted as text, is then well gzipped, which is not the case with base64. If you insert images into CSS via data: URI, use URIencoded, not base64.

We have not yet implemented this, we are implementing the next step. If for the first time we uploaded pictures to the user and realized that he has support for SVG or not, then with all the following requests we can look into cookies, and give the user who has support for SVG only such code. Users who do not have support for SVG, always give the picture. There is no point in the subsequent request to give and so and so.

The final point. Let's optimize JS. There is not a very rich choice. There is UglifyJS, use it, there are no other options.

I am almost done. I want to emphasize that all these techniques can be used not only when you are doing a straightforward super-optimized light version and when you need to meet the 10k contest. Most of them can be used on ordinary sites. They will make the site load faster, your users - happier, and you - maybe a little richer.

Source: https://habr.com/ru/post/336952/


All Articles