Intro

In the context of developing simple websites without using any frameworks, the problem of website's download speed does not seem to be so critical. But if you have an app and don’t want to make users with low internet speed, it’s important to optimize the application.

Any website can be optimized, following the next recommendations:

static-web-optimization

  1. All images on the web page should be compressed and optimized with the appropriate tools. In that case the following websites will be quite helpful: for .png follow the page TinyPNG and for .jpg - the page TinyJPG. For to minify .svg images there is a popular command line tool SVGO that you may have a look at the GitHub repository.

  2. The style responsible for displaying the page elements, visible to the user in the first place, should be taken from the connected CSS files into the internal styles. This will allow them to work right after loading the HTML document, without waiting for the files with external styles to be loaded by the browser.

  3. If the page includes a lot of images, you should think about the advisability of implementing the functionality for delaying loading of them. For example, images can be loaded while the user is scrolling the page.

  4. And of course, we should not forget about the most popular way to replace the white screen of the loading page with something more pleasant - remember to use loaders.

Following the abovementioned tips, you can significantly speed up, optimize and make more user-friendly almost any STATIC web page.

But what about the SPA? Let's try to consider this case on the example of the Angular application.

Angular SPA optimization

angular-spa-optimization

In the process of assembling the application, Webpack makes its compilation from the TypeScript into the Javascript, minification and the separation of the entire application to the bundles. Initially, as in the case of a static web page, the browser loads the document itself. The body of the document initially lacks HTML markup of the page and only contains a selector of the root Angular component and links to the bundles.

<body>
<app-root></app-root>
<script type="text/javascript"
src="runtime.a66f828dca56eeb90e02.js"></script>
<script type="text/javascript" src="polyfills.2f4a59095805af02bd79.js"></script>
<script type="text/javascript" src="main.d5dc696e4b174c63311e.js"></script>
</body>

When the document is loaded, the browser starts downloading of the bundles (main, inline, styles, polyfills etc…). And only after loading and executing all the bundles the launch of the Angular application will be initiated and the components will be displayed.

In case of a slow speed of the internet connection, the time spent for loading the Angular bundles often exceeds 10 seconds, that is a crime against UX.

In this case, the above mentioned recommendations for speeding up the loading of the website will not help us, except for using a loader. I think the reason is obvious:

  • Firstly, optimized images will load faster, but only AFTER launching Angular applications;

  • Secondly, internal styles also will not help, because the elements for which they are responsible will be rendered only AFTER the application's launch;

  • Thirdly, delayed loading of images will also work after rendering the component template only where they are used.

It turns out that it is the most rational and the easiest to implement to work with the "white screen" in the period before the launch of the SPA using the loader. In this case, there is no need to develop the logic for the transition from the display of the loader to the display of the application itself.

Namely, due to the fact that the HTML layout located inside the selector of the root component is visible to the user exactly before the Angular application starts working and renders the root component. Using this, we can arrange the layout of the loader inside the selector "app-root", and its styles - internally in the head of the document. In fact, in addition to the loader inside the selector, we can display anything within the capabilities of the static page. All this increases the volume of the original document loaded and ultimately postpones the launch of the Angular application.

Suppose that we need to display the logo of the company in addition to the loader. In accordance with the abovementioned, the logo should have a minimum size, and it is desirable to be presented as a string in the base64 encoding or as the inline SVG.

To do this, let's add the layout of the preloading screen inside the app-root tag:

...
<app-root>
  <div class="preview">
    <div class="logo">
      <img alt="Angular Logo" src="data:image/svg+xml;base64,
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9I
jAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMz
BMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjM
uMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjIt
LjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0a
CAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS
43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGw
xNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
    </div>
    <div class="loader">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>
  </div>
</app-root>
...

Here we have preview container with base64 encoded logo and animated loader. For the speedy display, we put the styles internally in the head of the document.

<head>
...
<style>
    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
    }

    .preview {
      position: relative;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 100vw;
      height: 100vh;
      margin: auto;
    }

    .logo {
      width: 285px;
      height: 300px;
      margin-bottom: 20px;
    }

    .loader {
      position: relative;
      display: flex;
      justify-content: space-between;
      align-items: stretch;
      flex-wrap: wrap;
      width: 109px;
      height: 109px;
    }

    .loader > div {
      position: relative;
      display: inline-block;
      width: 35px;
      height: 35px;
      background: tomato;
      transform: scale(0.0);
      transform-origin: center center;
      animation: loader 2s infinite linear;
    }

    .loader > div:nth-of-type(1),
    .loader > div:nth-of-type(5),
    .loader > div:nth-of-type(9) {
       animation-delay: 0.4s;
    }

    .loader > div:nth-of-type(4),
    .loader > div:nth-of-type(8) {
       animation-delay: 0.2s;
    }

    .loader > div:nth-of-type(2),
    .loader > div:nth-of-type(6) {
       animation-delay: 0.6s;
    }

    .loader > div:nth-of-type(3) {
       animation-delay: 0.8s;
    }

    @keyframes loader {
      0%   { transform: scale(0.0); }
      40%  { transform: scale(1.0); }
      80%  { transform: scale(1.0); }
      100% { transform: scale(0.0); }
    }
  </style>
</head>

Now, by downloading and running the Angular application, the user will see the preloading page, and as soon as the application is launched, the root component will be rendered instead.

angular-application-preloading-page

Conclusion

From this moment, you can start optimizing the page taking into account all the above mentioned recommendations. And again we will stop on the loader. The loader we just applied can no longer be used anywhere in the application. If there is a need to display the loader (for example, to indicate the execution time of the HTTP request), it will be necessary to create a new loader component, that will be part of the Angular application.

To test the work of the loader before launching the Angular application, open Chrome Dev Tools and throttle network speed to "Slow 3G".

In case you have any questions, feel free to contuct us.