Do you know Angular Universal? It can help the website provide better SEO support!
Generally speaking, ordinary Angular applications run in the browser, render pages in the DOM, and interact with users. Angular Universal renders it on the server (Server-Side Rendering, SSR), generates static application web pages, and then displays it on the client. The advantage is that it can render faster and provides content display to users before providing a complete interaction.
This article was completed in Angular 14 environment. Some content may not be applicable to new Angular versions. Please refer to the official Angular documentation.
Benefits of using SSR
Better SEO-friendly
Although some search engines and social media, including Google, now claim to support crawling SPA (Single-Page Application) applications powered by JavaScript (JS), the results seem unsatisfactory. The SEO performance of static HTML websites is still better than that of dynamic websites, which is also the view held by Angular’s official website (Angular is Google’s!).
Universal can generate static versions of applications without JS, and supports search, external links, and navigation better.
Improve mobile performance
Some mobile devices may not support JS or have very limited support for JS, resulting in a very poor access experience for the website. In this case, we need to provide applications without JS version to provide users with a better experience.
Show home page faster
For users' user experience, the speed of home page display is crucial. according toeBay's data, every 100 milliseconds increase in the display speed of search results, the usage rate of "Add to Cart" is increased by 0.5%.
After using Universal, the homepage of the application will be displayed to the user in a complete form. This is a pure HTML webpage that can be displayed even if it does not support JS. At this time, although the web page cannot handle browser events, it supports therouterLink
Make a jump.
The advantage of this is that we can first use static web pages to capture the user's attention, and load the entire Angular application while the user browses the web page. This gives users a very good and fast loading experience.
Add SSR to the project
The Angular CLI can help us to transform a normal Angular project into a project with SSR very easily. To create a server application, only one command is required:
ng add @nguniversal/express-engine
It is recommended to submit all changes before running this command.
This command will modify the project as follows:
-
Add server file:
-
- Server main program file
-
app/
- Server application main module -
- TypeScript server configuration file
-
- ExpressThe running file of web server
-
-
Modified file:
-
- Add dependencies and run scripts required for SSR
-
- Add the configuration required to develop and build SSR applications
-
Replace the browser API
Since Universal applications are not executed in the browser, some browsers' APIs or features will not be available. For example, a server application cannot use a global object in the browserwindow
、document
,navigator
,location
。
Angular provides two injectable objects for replacing peer objects on the server:Location
andDOCUMENT
。
For example, in the browser, we passAfter obtaining the address of the current browser and changing it to SSR, the code is as follows:
import { Location } from '@angular/common'; export class AbmNavbarComponent implements OnInit{ // Inject Location into ctor constructor(private _location:Location){ //... } ngOnInit() { // Print the current address (this._location.path(true)); } }
Similarly, for use in the browser()
Get the DOM element, and after changing it to SSR, the code is as follows:
import { DOCUMENT } from '@angular/common'; export class AbmFoxComponent implements OnInit{ // Inject DOCUMENT into ctor constructor(@Inject(DOCUMENT) private _document: Document) { } ngOnInit() { // Get the DOM with id of fox-container const container = this._document.getElementById('fox-container'); } }
Use URL Absolute address
In Angular SSR applications, the URL address for HTTP requests must be an absolute address (i.e.http/https
The address at the beginning cannot be a relative address, such as/api/heros
). Angular official recommendation to set the requested URL to the full pathrenderModule()
orrenderModuleFactory()
ofoptions
In the parameters. However, in the code automatically generated by v14, there is no code that explicitly calls these two methods. The same effect can be achieved by intercepting Http requests.
Let's prepare an interceptor first, assuming the file is located in the projectshared/
path:
import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Inject, Injectable, Optional } from '@angular/core'; import { REQUEST } from '@nguniversal/express-engine/tokens'; import { Request } from 'express'; // Ignore case checkingconst startsWithAny = (arr: string[] = []) => (value = '') => { return (test => ().startsWith(())); }; // http, https, relative protocol addressconst isAbsoluteURL = startsWithAny(['http', '//']); @Injectable() export class UniversalRelativeInterceptor implements HttpInterceptor { constructor(@Optional() @Inject(REQUEST) protected request: Request) { } intercept(req: HttpRequest<any>, next: HttpHandler) { // URL that is not an absolute address if (!isAbsoluteURL()) { let protocolHost: string; if () { // If the injected REQUEST is not empty, get the protocol and address from the injected SSR REQUEST protocolHost = `${}://${( 'host' )}`; } else { // If the injected REQUEST is empty, for example, prerender build: // You need to add a custom address prefix here, for example, our requests are all from. protocolHost = ''; } const pathSeparator = !('/') ? '/' : ''; const url = protocolHost + pathSeparator + ; const serverRequest = ({ url }); return (serverRequest); } else { return (req); } } } import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Inject, Injectable, Optional } from '@angular/core'; import { REQUEST } from '@nguniversal/express-engine/tokens'; import { Request } from 'express'; // Ignore case checkingconst startsWithAny = (arr: string[] = []) => (value = '') => { return (test => ().startsWith(())); }; // http, https, relative protocol addressconst isAbsoluteURL = startsWithAny(['http', '//']); @Injectable() export class UniversalRelativeInterceptor implements HttpInterceptor { constructor(@Optional() @Inject(REQUEST) protected request: Request) { } intercept(req: HttpRequest<any>, next: HttpHandler) { // URL that is not an absolute address if (!isAbsoluteURL()) { let protocolHost: string; if () { // If the injected REQUEST is not empty, get the protocol and address from the injected SSR REQUEST protocolHost = `${}://${( 'host' )}`; } else { // If the injected REQUEST is empty, for example, prerender build: // You need to add a custom address prefix here, for example, our requests are all from. protocolHost = ''; } const pathSeparator = !('/') ? '/' : ''; const url = protocolHost + pathSeparator + ; const serverRequest = ({ url }); return (serverRequest); } else { return (req); } } }
Then inProvide in the file:
import { UniversalRelativeInterceptor } from './shared/'; // ... Other imports @NgModule({ imports: [ AppModule, ServerModule, // If you use @angular/flext-layout, you also need to introduce a server module here FlexLayoutServerModule, ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: UniversalRelativeInterceptor, multi: true } ], bootstrap: [AppComponent], }) export class AppServerModule { }
In this way, any request for relative addresses will be automatically converted into absolute address requests, and there will be no problems in SSR scenarios.
Prerender Prerender Static HTML
After the above steps, if we passnpm run build:ssr
Build the project and you will finddist/<your project>/browser
OnlyFile, open the file to view it, and find that there are some
<app-root></app-root>
Such elements, that is, your web page content is not generated in html. This is because Angular uses dynamic routing, e.g./product/:id
This kind of routing, and the rendering results of the page must be performed by JS before they can be known. Therefore, Angular uses Express as a web server, and can use the template engine to generate a static HTML interface based on user requests (crawler requests) when running on the server.
andprerender
(npm run prerender
) generates a static HTML file at build time. For example, when we do an official website of the enterprise, there are only a few pages, then we can use pre-rendering technology to generate static HTML files of these pages to avoid dynamic generation at runtime, thereby further improving the access speed and user experience of web pages.
Pre-rendering path configuration
The web page path that requires pre-rendering (pre-compiled HTML) can be provided in several ways:
1. Additional parameters through the command line:
ng run <app-name>:prerender --routes /product/1 /product/2
2. If there are many paths, for exampleproduct/:id
This dynamic path can be used with a path file:
/products/1 /products/23 /products/145 /products/555
Then specify the file in the command line parameters:
ng run <app-name>:prerender --routes-file
3. In the projectThe path required for file configuration:
"prerender": { "builder": "@nguniversal/builders:prerender", "options": { "routes": [ // Configure here "/", "/main/home", "/main/service", "/main/team", "/main/contact" ] },
After the configuration is complete, re-execute the pre-rendering command (npm run prerender
Or use the command line parameters and execute it according to the command in <1><2> above), after the compilation is completed, then open itdist/<your project>/browser
NextYou will find that there is nothing inside
<app-root></app-root>
, replaced by the actual content of the homepage. At the same time, the corresponding path directory and the corresponding path directory are generated.Subpage file.
SEO Optimization
The key to SEO is to use the web pagetitle
,keywords
anddescription
, so for web pages we want to have search engines include, we can modify the code to provide these contents.
In Angular 14, if the routing interface passesRoutes
Configure the web page to statictitle
Write it directly in the routing configuration:
{ path: 'home', component: AbmHomeComponent, title: '<Title you want to display on the browser tab>' },
In addition, Angular also provides injectableTitle
andMeta
Used to modify the title and meta information of the web page:
import { Meta, Title } from '@angular/platform-browser'; export class AbmHomeComponent implements OnInit { constructor( private _title: Title, private _meta: Meta, ) { } ngOnInit() { this._title.setTitle('<Title of this page>'); this._meta.addTags([ { name: 'keywords', content: '<keywords for this page, separated by English commas>' }, { name: 'description', content: '<Description of this page>' } ]); } }
Summarize
As a SPA enterprise-level development framework, Angular has its own unique advantages in modular and team development. Unreliable in Evolution to v14NgModule
IndependenceComponent
Functions further simplify the modular architecture.
Angular Universal focuses on how to render and generate static HTML on the server side of Angular App. SSR is not recommended for SPAs with complex user interactions. Universal and SSR technologies can be considered for websites or systems that have small page counts and SEO requirements.