Content delivery networks
When you take your site to production, the static assets should be uploaded somewhere on the Internet. You may be used to putting them on the same server where all your dynamic HTML is generated. Our example so far has also used this approach: the Node / Express server, launched by the node meadowlark.js command, serves all kinds of HTML as well as static resources. However, if you want to optimize the performance of your site (or lay it in the future), you will need the ability to host static resources on a content delivery network (CDN). CDN is a server optimized for delivering static resources. It uses special headers (which we'll learn more about soon) that enable browser caching.
Additionally, a CDN can include geographic optimization (often called edge caching), which means that static content will be delivered from the server closest to your client. Although the Internet is very fast (of course, it does not work at the speed of light, but at close to it), data will be delivered even faster from a distance of hundreds of kilometers than thousands. The time savings are insignificant in each individual case, but when multiplied by the number of users, requests, and resources, it quickly becomes impressive.
Most of your static resources will be referenced in HTML views
<link> CSS-, <script> — JavaScript, <img> ,
It is a common practice to have static links in CSS, usually in the background-image property. Finally, static resources are sometimes referenced in JavaScript, for example JavaScript code can dynamically change or insert tags.
<img>
or the background-image properties.
Don't worry about cross-domain resource sharing (CORS) when using a CDN. External resources loaded in HTML are not subject to CORS rules: you only need to enable CORS for resources loaded via Ajax (see Chapter 15).
Designing for a CDN
How you use a CDN depends on the architecture of your site. Most content delivery networks allow you to set routing rules to determine where to send incoming requests. While you can set fairly complex routing rules, it usually boils down to sending static resource requests to one location (usually provided by your CDN) and dynamic endpoint requests (dynamic pages or API endpoints) to another.
Choosing and configuring a CDN is a vast topic that I won't cover here, but I'll give you some basic information to help you set up your CDN of choice.
The simplest approach to structuring your application is to make dynamic and static resources easily distinguishable. This will keep your CDN routing rules as simple as possible. While this can be achieved using subdomains (for example, dynamic resources are served from meadowlark.com and static resources from static.meadowlark.com), this approach adds additional complexity and complicates local development. An easier way is to use request paths: for example, everything that starts with / public / is static resources, and everything else is dynamic. The approach might be different if you are generating content with Express or using Express to provide an API for a single page application.
Server side rendering website
If you use Express to render dynamic HTML (in other words, everything that starts with / static /) is static resources, and everything else is dynamic. With this approach, all of your (dynamically generated) URLs will be as you want them to be (unless they start with / static /, of course!), And all your static resources will be prefixed with / static /:
<img src="/static/img/meadowlark-logo-1.png" alt="Meadowlark Logo">
<a href="/about">Meadowlark Travel</a>.
So far in this book, we have used static middleware, as if all static resources were laid out in the root directory. Thus, by placing the static resource foo.png in the public folder, we refer to it at the URL path /foo.png, not /static/foo.png. Of course, it is possible to create a static subdirectory inside the existing public directory and the URL for /public/static/foo.png will be /static/foo.png, but this is not very smart. Fortunately, the static middleware allows us to avoid this by specifying a different path when calling app.use:
app.use('/static', express.static('public'))
Now, in the development environment, we can use the same URL structure as in the exploitation. If the contents of the public folder and the CDN are in sync, you can reference static resources in both places and seamlessly switch between development and production.
When setting up routing for the CDN (you will need to refer to your CDN documentation), the routing will look like this.
Single Page Applications
Single Page Applications are generally the opposite of server-side rendered websites: only the API will be passed to the server (for example, any request starting with / api), everything else is passed to the static file storage.
In Chapter 16, you saw that you can create an assembly to operate your application, which will include all the static resources loaded into the CDN. Then you just need to make sure that your API routing setup is correct. This way you will have the following routing.
Now that we've learned how to structure an application to seamlessly transition from development to production, let's look at what actually happens in caching and how it optimizes performance.
Caching Static Resources
Whether you're using Express or CDN to serve static resources, it's worth understanding the HTTP request headers that the browser uses to determine when and how to cache static resources.
- Expires / Cache-Control. These two headers tell your browser the maximum amount of time a resource can be cached. The browser takes them seriously: if they tell it to store something for a month, it simply won't download it again for a month, as long as it remains in the cache. It is important to understand that the browser may delete an image from the cache before the expiration date for reasons that you cannot control. For example, the user can manually clear the cache, or the browser can delete your resource to make room for the resources the user visits more often. You only need one of these headers. Expires is more widely supported, so it is preferable to use it. If the resource is in the cache and has not yet expired,the browser will not execute the GET request at all, which will improve performance, especially on mobile devices.
- Last-Modified / ETag. Provide a kind of version control: if the browser needs to check out a resource, it will check those tags before loading the content. The GET request to the server will still be executed, but if the values returned in these headers demonstrate to the browser that the resource has not changed, the browser will not proceed to download the file. As the name suggests, Last-Modified allows you to set the date when an asset was last modified. ETag allows you to use an arbitrary string, usually a version string or a content hash.
When issuing static resources, use the Expires header and either Last-Modified or ETag. Express's built-in static middleware installs Cache-Control but does not process either Last-Modified or ETag. Therefore, it is suitable for development, but not for operation.
If you choose to host your static assets on a CDN, such as Amazon CloudFront, Microsoft Azure, Fastly, Cloudflare, Akamai, or StackPath, then you get a bonus: most of these details will be handled for you. You will be able to make fine adjustments, although the default settings in any of these services are also fine.
Modifying static content
Caching significantly improves your site's performance, but not without consequences. In particular, if any of the static resources change, clients may not see the change until the browser cached versions have expired. Google recommends caching for one month, preferably one year. Imagine a user who visits your site every day through the same browser: he can see your updates only after a year!
Obviously, this is not a desirable situation, but you cannot tell your users to clear the cache. The solution to this problem is to disable cache busting. This trick will give you control over when the browser should reload the resource. This method simply adds some version information to the file name. When you update a resource, the name of the resource changes and the browser knows to download it. Typically, this is tantamount to controlling the versions of the resource (main.2.css or main.css? Version = 2) or adding some kind of hash (main.e16b7e149dccfcc399e025e0c454bf77.css). Whichever method you use, when you update a resource, its name changes and the browser knows to load it.
You can do the same with multimedia resources. Let's take our logo (/static/img/meadowlark_logo.png). If we put it on a CDN to maximize performance by setting the retention period to one year and then changing the logo, users might not see the change for a year. However, if we rename the logo to /static/img/meadowlark_logo-1.png and reflect this change in HTML, the browser will have to download it as it appears to be a new resource.
If you settled on a single page application framework such as create-react-app or similar, then at the build stage an assembly of ready-to-use resources will be created with hashes added to them.
If you're starting from scratch, you probably want to take a look at builders (that's what's under the hood of single page application frameworks). JavaScript, CSS, and some other types of static resources will be bundled into as few assemblies as possible, and the result will be minimized as much as possible. Build customization is a vast topic, but luckily there is a lot of good documentation on it. Below are the most popular collectors currently available.
- Webpack (https://webpack.js.org/). One of the first collectors to achieve real progress. He still has many supporters. It is very difficult, and there is a price to pay for that complexity: the learning curve is steep. However, this packer is good for learning the basics.
- Parcel (https://parceljs.org/). Appeared recently and made a lot of noise. It is extremely well documented, very fast, and most importantly, it has the shortest learning curve. It is suitable if you need to get the job done quickly and without hassle.
- Rollup (https://rollupjs.org/). It sits somewhere between Webpack and Parcel. Like Webpack, it is robust and feature rich. It's easier to get started with than Webpack, however, but not as easy as Parcel.
Summary
For all its apparent simplicity, static resources are a lot of hassle. However, they make up the bulk of the data transmitted to your visitors, so the time spent optimizing them will pay off with interest.
A viable solution for static assets not previously mentioned is to expose static assets to a CDN and always use the full URL to the asset in views and CSS. This approach has the advantage of being very simple, but if you ever wish to host a weeklong hackathon in a forest hut with no internet access, you're in trouble!
Careful assembly and minification is another area where you can save time if your application's performance gains don't justify the effort. In particular, if you only have one or two JavaScript files on your site, and all the CSS is in the same file, you can skip building altogether, but real applications tend to grow over time.
Whichever method you choose for serving static resources, I advise you to upload them separately, preferably on a CDN. If it seems to you that it is troublesome, I assure you: it is not at all as difficult as it seems. Especially if you spend a little time on the deployment system beforehand so that the deployment of static resources to one location and applications to another will be automatic.
If you are concerned about the cost of hosting on a CDN, I urge you to take a look at the amounts you are currently paying for hosting. Most hosting providers charge big bucks for traffic even if you don't know about it. However, if suddenly your site is listed on Slashdot and you experience the slashdot effect, you may receive a completely unexpected hosting bill. CDN hosting is usually set up in such a way that you only pay for what you use. For example, the site I run for a local midsize company uses roughly 20GB of traffic per month, while the static hosting fees (which is a very media-heavy site) are only a couple of dollars per month.
The benefits of hosting static resources on a CDN are substantial, and the cost and inconvenience of doing so is minimal, so I strongly advise you to choose this path.
Through certain JavaScript tricks in the browser, it is possible to use uncompiled LESS. However, this approach can lead to negative consequences in terms of performance, so I do not recommend using it.
More details about the book can be found on the website of the publishing house
» Table of Contents
» Excerpt
For Habitants a 25% discount on coupon - JavaScript
Upon payment for the paper version of the book, an e-book is sent by e-mail.