Overview
Angular Universal is a library that can be used to render an Angular 2 app on the server.
Why Universal?
There are four primary reasons why you would want to use Angular Universal:
- Better Perceived Performance - First time users of your application will instantly see a server rendered view which greatly improves perceived performance and the overall user experience. According to research at Google, the difference of just 200 milliseconds in page load performance has an impact on user behavior.
- Optimized for Search Engines - Although Googlebot crawls and renders most dynamic sites, many search engines expect plain HTML. Server-side pre-rendering is a reliable, flexible and efficient way to ensure that all search engines can access your content.
- Site Preview - Ensure that Facebook, Twitter and all other social media apps correctly display a preview image of your app.
- Graceful Degradation - An IE6 user likely will have issues with your client side Angular 2 web app, but as a fall back you can have those users only see the server rendered page.
In essence, this means that Universal is extremely important if you have a public web app with anonymous content (i.e. an anonymous user on the internet can browse to a particular page.
How does it work?
At a high level, there are two primary pieces of Angular Universal:
- Server - Rendering HTML and inline Preboot code on the server for a given URL request. There are two variations of this:
- pre-render - Generate static HTML at build time that you can deploy to a CDN or static web host
- re-render - Run application code on the server with each request to generate the server view on the fly
- Browser - Transitioning from the server generated view to the client generated view in the browser. This is where preboot comes in:
- Browser receives initial payload from server
- User sees server view
- Preboot creates hidden div that will be used for client bootstrap and starts recording events
- Browser makes async requests for additional assets (i.e. images, JS, CSS, etc.)
- Once external resources loaded, Angular client bootstrapping begins
- Client view rendered to the hidden div created by Preboot
- Bootstrap complete, so Angular client calls preboot.done()
- Preboot events replayed in order to adjust the application state to reflect changes made by the user before Angular bootstrapped (i.e. typing in textbox, clicking button, etc.)
- Preboot switches the hidden client view div for the visible server view div
- Finally, Preboot performs some cleanup on the visible client view including setting focus
What about caching?
When you are dealing with server rendering, caching at many different levels is extremely important. In fact, it is our recommendation that your first option should be using Angular Universal pre-rendering to generate static HTML files so you don't need any dynamic rendering at run time and everything can be cached on a CDN. This, however, is only appropriate for certain use cases.
When dynamic server rendering is needed, you can leverage the following types of caching:
- Page caching - Use a service like CloudFlare or Akamai to cache the dynamically generated page for short periods of time
- Object caching - Angular's AoT compiler for Universal enables you to generate artifacts from your application code that can be reused among many server requests
- Data caching - Your Universal app on the server side will pull data from your API and it is possible to share that data with the browser client app so that the client doesn't have to pull the data again.
Currently, only object caching is handled by Angular Universal out of the box. We will be adding features in the future to make data caching easier, but the basic gist is:
- On the server side put data into the generated html (i.e.
<script>var myData = {}</script>
) - On the client side have your state management system first check that global data object before pulling data again.
If you do this and your data contains user-specific data, however, just be aware that you will not be able to do any page-level caching since you should not share user-specific data across different users.