5 lines
24 KiB
JSON
5 lines
24 KiB
JSON
{
|
|
"id": "guide/universal",
|
|
"title": "Server-side rendering (SSR) with Angular Universal",
|
|
"contents": "\n\n\n<div class=\"github-links\">\n <a href=\"https://github.com/angular/angular/edit/master/aio/content/guide/universal.md?message=docs%3A%20describe%20your%20change...\" aria-label=\"Suggest Edits\" title=\"Suggest Edits\"><i class=\"material-icons\" aria-hidden=\"true\" role=\"img\">mode_edit</i></a>\n</div>\n\n\n<div class=\"content\">\n <h1 id=\"server-side-rendering-ssr-with-angular-universal\">Server-side rendering (SSR) with Angular Universal<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#server-side-rendering-ssr-with-angular-universal\"><i class=\"material-icons\">link</i></a></h1>\n<p>This guide describes <strong>Angular Universal</strong>, a technology that renders Angular applications on the server.</p>\n<p>A normal Angular application executes in the <em>browser</em>, rendering pages in the DOM in response to user actions.\nAngular Universal executes on the <em>server</em>, generating <em>static</em> application pages that later get bootstrapped on\nthe client. This means that the application generally renders more quickly, giving users a chance to view the application\nlayout before it becomes fully interactive.</p>\n<p>For a more detailed look at different techniques and concepts surrounding SSR, please check out this\n<a href=\"https://developers.google.com/web/updates/2019/02/rendering-on-the-web\">article</a>.</p>\n<p>You can easily prepare an app for server-side rendering using the <a href=\"guide/glossary#cli\">Angular CLI</a>.\nThe CLI schematic <code>@nguniversal/express-engine</code> performs the required steps, as described below.</p>\n<div class=\"alert is-helpful\">\n<p> <strong>Note:</strong> <live-example downloadonly=\"\">Download the finished sample code</live-example>,\nwhich runs in a <a href=\"https://expressjs.com/\">Node.js® Express</a> server.</p>\n</div>\n<a id=\"the-example\"></a>\n<h2 id=\"universal-tutorial\">Universal tutorial<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#universal-tutorial\"><i class=\"material-icons\">link</i></a></h2>\n<p>The <a href=\"tutorial\">Tour of Heroes tutorial</a> is the foundation for this walkthrough.</p>\n<p>In this example, the Angular CLI compiles and bundles the Universal version of the app with the\n<a href=\"guide/aot-compiler\">Ahead-of-Time (AOT) compiler</a>.\nA Node.js Express web server compiles HTML pages with Universal based on client requests.</p>\n<p>To create the server-side app module, <code>app.server.module.ts</code>, run the following CLI command.</p>\n<code-example language=\"bash\">\n\nng add @nguniversal/express-engine\n\n</code-example>\n<p>The command creates the following folder structure.</p>\n<code-example language=\"none\">\nsrc/\n index.html <i>app web page</i>\n main.ts <i>bootstrapper for client app</i>\n main.server.ts <i>* bootstrapper for server app</i>\n style.css <i>styles for the app</i>\n app/ ... <i>application code</i>\n app.server.module.ts <i>* server-side application module</i>\nserver.ts <i>* express web server</i>\ntsconfig.json <i>TypeScript base configuration</i>\ntsconfig.app.json <i>TypeScript browser application configuration</i>\ntsconfig.server.json <i>TypeScript server application configuration</i>\ntsconfig.spec.json <i>TypeScript tests configuration</i>\n</code-example>\n<p>The files marked with <code>*</code> are new and not in the original tutorial sample.</p>\n<h3 id=\"universal-in-action\">Universal in action<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#universal-in-action\"><i class=\"material-icons\">link</i></a></h3>\n<p>To start rendering your app with Universal on your local system, use the following command.</p>\n<code-example language=\"bash\">\nnpm run dev:ssr\n</code-example>\n<p>Open a browser and navigate to <a href=\"http://localhost:4200/\">http://localhost:4200/</a>.\nYou should see the familiar Tour of Heroes dashboard page.</p>\n<p>Navigation via <code>routerLinks</code> works correctly because they use the native anchor (<code><a></code>) tags.\nYou can go from the Dashboard to the Heroes page and back.\nYou can click a hero on the Dashboard page to display its Details page.</p>\n<p>If you throttle your network speed so that the client-side scripts take longer to download (instructions below),\nyou'll notice:</p>\n<ul>\n<li>Clicking a hero on the Heroes page does nothing.</li>\n<li>You can't add or delete a hero.</li>\n<li>The search box on the Dashboard page is ignored.</li>\n<li>The <em>Back</em> and <em>Save</em> buttons on the Details page don't work.</li>\n</ul>\n<p>User events other than <code><a href=\"api/router/RouterLink\" class=\"code-anchor\">routerLink</a></code> clicks aren't supported.\nYou must wait for the full client app to bootstrap and run, or buffer the events using libraries like\n<a href=\"https://github.com/angular/preboot\">preboot</a>, which allow you to replay these events once the client-side scripts load.</p>\n<p>The transition from the server-rendered app to the client app happens quickly on a development machine, but you should\nalways test your apps in real-world scenarios.</p>\n<p>You can simulate a slower network to see the transition more clearly as follows:</p>\n<ol>\n<li>Open the Chrome Dev Tools and go to the Network tab.</li>\n<li>Find the <a href=\"https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#throttling\">Network Throttling</a>\ndropdown on the far right of the menu bar.</li>\n<li>Try one of the \"3G\" speeds.</li>\n</ol>\n<p>The server-rendered app still launches quickly but the full client app may take seconds to load.</p>\n<a id=\"why-do-it\"></a>\n<h2 id=\"why-use-server-side-rendering\">Why use server-side rendering?<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#why-use-server-side-rendering\"><i class=\"material-icons\">link</i></a></h2>\n<p>There are three main reasons to create a Universal version of your app.</p>\n<ol>\n<li>Facilitate web crawlers through <a href=\"https://static.googleusercontent.com/media/www.google.com/en//webmasters/docs/search-engine-optimization-starter-guide.pdf\">search engine optimization (SEO)</a></li>\n<li>Improve performance on mobile and low-powered devices</li>\n<li>Show the first page quickly with a <a href=\"https://developers.google.com/web/tools/lighthouse/audits/first-contentful-paint\">first-contentful paint (FCP)</a></li>\n</ol>\n<a id=\"seo\"></a>\n<a id=\"web-crawlers\"></a>\n<h3 id=\"facilitate-web-crawlers-seo\">Facilitate web crawlers (SEO)<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#facilitate-web-crawlers-seo\"><i class=\"material-icons\">link</i></a></h3>\n<p>Google, Bing, Facebook, Twitter, and other social media sites rely on web crawlers to index your application content and\nmake that content searchable on the web.\nThese web crawlers may be unable to navigate and index your highly interactive Angular application as a human user could do.</p>\n<p>Angular Universal can generate a static version of your app that is easily searchable, linkable, and navigable without JavaScript.\nUniversal also makes a site preview available since each URL returns a fully rendered page.</p>\n<a id=\"no-javascript\"></a>\n<h3 id=\"improve-performance-on-mobile-and-low-powered-devices\">Improve performance on mobile and low-powered devices<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#improve-performance-on-mobile-and-low-powered-devices\"><i class=\"material-icons\">link</i></a></h3>\n<p>Some devices don't support JavaScript or execute JavaScript so poorly that the user experience is unacceptable.\nFor these cases, you may require a server-rendered, no-JavaScript version of the app.\nThis version, however limited, may be the only practical alternative for\npeople who otherwise couldn't use the app at all.</p>\n<a id=\"startup-performance\"></a>\n<h3 id=\"show-the-first-page-quickly\">Show the first page quickly<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#show-the-first-page-quickly\"><i class=\"material-icons\">link</i></a></h3>\n<p>Displaying the first page quickly can be critical for user engagement.\nPages that load faster perform better, <a href=\"https://web.dev/shopping-for-speed-on-ebay/\">even with changes as small as 100ms</a>.\nYour app may have to launch faster to engage these users before they decide to do something else.</p>\n<p>With Angular Universal, you can generate landing pages for the app that look like the complete app.\nThe pages are pure HTML, and can display even if JavaScript is disabled.\nThe pages don't handle browser events, but they <em>do</em> support navigation through the site using <a href=\"guide/router#router-link\"><code>routerLink</code></a>.</p>\n<p>In practice, you'll serve a static version of the landing page to hold the user's attention.\nAt the same time, you'll load the full Angular app behind it.\nThe user perceives near-instant performance from the landing page\nand gets the full interactive experience after the full app loads.</p>\n<a id=\"how-does-it-work\"></a>\n<h2 id=\"universal-web-servers\">Universal web servers<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#universal-web-servers\"><i class=\"material-icons\">link</i></a></h2>\n<p>A Universal web server responds to application page requests with static HTML rendered by the <a href=\"guide/universal#universal-engine\">Universal template engine</a>.\nThe server receives and responds to HTTP requests from clients (usually browsers), and serves static assets such as scripts, CSS, and images.\nIt may respond to data requests, either directly or as a proxy to a separate data server.</p>\n<p>The sample web server for this guide is based on the popular <a href=\"https://expressjs.com/\">Express</a> framework.</p>\n<div class=\"alert is-helpful\">\n<p> <strong>Note:</strong> <em>Any</em> web server technology can serve a Universal app as long as it can call Universal's <code><a href=\"api/platform-server/renderModule\" class=\"code-anchor\">renderModule</a>()</code> function.\nThe principles and decision points discussed here apply to any web server technology.</p>\n</div>\n<p>Universal applications use the Angular <code>platform-server</code> package (as opposed to <code>platform-browser</code>), which provides\nserver implementations of the DOM, <code>XMLHttpRequest</code>, and other low-level features that don't rely on a browser.</p>\n<p>The server (<a href=\"https://expressjs.com/\">Node.js Express</a> in this guide's example)\npasses client requests for application pages to the NgUniversal <code>ngExpressEngine</code>. Under the hood, this\ncalls Universal's <code><a href=\"api/platform-server/renderModule\" class=\"code-anchor\">renderModule</a>()</code> function, while providing caching and other helpful utilities.</p>\n<p>The <code><a href=\"api/platform-server/renderModule\" class=\"code-anchor\">renderModule</a>()</code> function takes as inputs a <em>template</em> HTML page (usually <code>index.html</code>),\nan Angular <em>module</em> containing components, and a <em>route</em> that determines which components to display.\nThe route comes from the client's request to the server.</p>\n<p>Each request results in the appropriate view for the requested route.\nThe <code><a href=\"api/platform-server/renderModule\" class=\"code-anchor\">renderModule</a>()</code> function renders the view within the <code><app></code> tag of the template,\ncreating a finished HTML page for the client.</p>\n<p>Finally, the server returns the rendered page to the client.</p>\n<h3 id=\"working-around-the-browser-apis\">Working around the browser APIs<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#working-around-the-browser-apis\"><i class=\"material-icons\">link</i></a></h3>\n<p>Because a Universal app doesn't execute in the browser, some of the browser APIs and capabilities may be missing on the server.</p>\n<p>For example, server-side applications can't reference browser-only global objects such as <code>window</code>, <code>document</code>, <code>navigator</code>, or <code>location</code>.</p>\n<p>Angular provides some injectable abstractions over these objects, such as <a href=\"api/common/Location\"><code>Location</code></a>\nor <a href=\"api/common/DOCUMENT\"><code>DOCUMENT</code></a>; it may substitute adequately for these APIs.\nIf Angular doesn't provide it, it's possible to write new abstractions that delegate to the browser APIs while in the browser\nand to an alternative implementation while on the server (aka shimming).</p>\n<p>Similarly, without mouse or keyboard events, a server-side app can't rely on a user clicking a button to show a component.\nThe app must determine what to render based solely on the incoming client request.\nThis is a good argument for making the app <a href=\"guide/router\">routable</a>.</p>\n<a id=\"universal-engine\"></a>\n<h3 id=\"universal-template-engine\">Universal template engine<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#universal-template-engine\"><i class=\"material-icons\">link</i></a></h3>\n<p>The important bit in the <code>server.ts</code> file is the <code>ngExpressEngine()</code> function.</p>\n<code-example path=\"universal/server.ts\" header=\"server.ts\" region=\"ngExpressEngine\">\n// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)\nserver.engine('html', ngExpressEngine({\n bootstrap: AppServerModule,\n}));\n\n</code-example>\n<p>The <code>ngExpressEngine()</code> function is a wrapper around Universal's <code><a href=\"api/platform-server/renderModule\" class=\"code-anchor\">renderModule</a>()</code> function which turns a client's\nrequests into server-rendered HTML pages. It accepts an object with the following properties:</p>\n<ul>\n<li><code>bootstrap</code>: The root <code><a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a></code> or <code><a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a></code> factory to use for bootstraping the app when rendering on the server. For the example app, it is <code>AppServerModule</code>. It's the bridge between the Universal server-side renderer and the Angular application.</li>\n<li><code>extraProviders</code>: This is optional and lets you specify dependency providers that apply only when rendering the app on the server. You can do this when your app needs information that can only be determined by the currently running server instance.</li>\n</ul>\n<p>The <code>ngExpressEngine()</code> function returns a <code>Promise</code> callback that resolves to the rendered page.\nIt's up to the engine to decide what to do with that page.\nThis engine's <code>Promise</code> callback returns the rendered page to the web server,\nwhich then forwards it to the client in the HTTP response.</p>\n<div class=\"alert is-helpful\">\n<p> <strong>Note:</strong> These wrappers help hide the complexity of the <code><a href=\"api/platform-server/renderModule\" class=\"code-anchor\">renderModule</a>()</code> function. There are more wrappers\nfor different backend technologies at the <a href=\"https://github.com/angular/universal\">Universal repository</a>.</p>\n</div>\n<h3 id=\"filtering-request-urls\">Filtering request URLs<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#filtering-request-urls\"><i class=\"material-icons\">link</i></a></h3>\n<p>NOTE: The basic behavior described below is handled automatically when using the NgUniversal Express schematic. This\nis helpful when trying to understand the underlying behavior or replicate it without using the schematic.</p>\n<p>The web server must distinguish <em>app page requests</em> from other kinds of requests.</p>\n<p>It's not as simple as intercepting a request to the root address <code>/</code>.\nThe browser could ask for one of the application routes such as <code>/dashboard</code>, <code>/heroes</code>, or <code>/detail:12</code>.\nIn fact, if the app were only rendered by the server, <em>every</em> app link clicked would arrive at the server\nas a navigation URL intended for the router.</p>\n<p>Fortunately, application routes have something in common: their URLs lack file extensions.\n(Data requests also lack extensions but they're easy to recognize because they always begin with <code>/api</code>.)\nAll static asset requests have a file extension (such as <code>main.js</code> or <code>/node_modules/zone.js/bundles/zone.umd.js</code>).</p>\n<p>Because we use routing, we can easily recognize the three types of requests and handle them differently.</p>\n<ol>\n<li><strong>Data request</strong>: request URL that begins <code>/api</code>.</li>\n<li><strong>App navigation</strong>: request URL with no file extension.</li>\n<li><strong>Static asset</strong>: all other requests.</li>\n</ol>\n<p>A Node.js Express server is a pipeline of middleware that filters and processes requests one after the other.\nYou configure the Node.js Express server pipeline with calls to <code>server.get()</code> like this one for data requests.</p>\n<code-example path=\"universal/server.ts\" header=\"server.ts (data URL)\" region=\"data-request\">\n// TODO: implement data requests securely\nserver.get('/api/**', (req, res) => {\n res.status(404).send('data requests are not yet supported');\n});\n\n</code-example>\n<div class=\"alert is-helpful\">\n<p> <strong>Note:</strong> This sample server doesn't handle data requests.</p>\n<p> The tutorial's \"in-memory web API\" module, a demo and development tool, intercepts all HTTP calls and\nsimulates the behavior of a remote data server.\nIn practice, you would remove that module and register your web API middleware on the server here.</p>\n</div>\n<p>The following code filters for request URLs with no extensions and treats them as navigation requests.</p>\n<code-example path=\"universal/server.ts\" header=\"server.ts (navigation)\" region=\"navigation-request\">\n// All regular routes use the Universal engine\nserver.get('*', (req, res) => {\n res.render(indexHtml, { req, providers: [{ provide: <a href=\"api/common/APP_BASE_HREF\" class=\"code-anchor\">APP_BASE_HREF</a>, useValue: req.baseUrl }] });\n});\n\n</code-example>\n<h3 id=\"serving-static-files-safely\">Serving static files safely<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#serving-static-files-safely\"><i class=\"material-icons\">link</i></a></h3>\n<p>A single <code>server.use()</code> treats all other URLs as requests for static assets\nsuch as JavaScript, image, and style files.</p>\n<p>To ensure that clients can only download the files that they are permitted to see, put all client-facing asset files in\nthe <code>/dist</code> folder and only honor requests for files from the <code>/dist</code> folder.</p>\n<p>The following Node.js Express code routes all remaining requests to <code>/dist</code>, and returns a <code>404 - NOT FOUND</code> error if the\nfile isn't found.</p>\n<code-example path=\"universal/server.ts\" header=\"server.ts (static files)\" region=\"static\">\n// Serve <a href=\"api/upgrade/static\" class=\"code-anchor\">static</a> files from /<a href=\"api/animations/browser\" class=\"code-anchor\">browser</a>\nserver.get('*.*', express.static(distFolder, {\n maxAge: '1y'\n}));\n\n</code-example>\n<h3 id=\"using-absolute-urls-for-http-data-requests-on-the-server\">Using absolute URLs for HTTP (data) requests on the server<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/universal#using-absolute-urls-for-http-data-requests-on-the-server\"><i class=\"material-icons\">link</i></a></h3>\n<p>The tutorial's <code>HeroService</code> and <code>HeroSearchService</code> delegate to the Angular <code><a href=\"api/common/http/HttpClient\" class=\"code-anchor\">HttpClient</a></code> module to fetch application data.\nThese services send requests to <em>relative</em> URLs such as <code>api/heroes</code>.\nIn a server-side rendered app, HTTP URLs must be <em>absolute</em> (for example, <code>https://my-server.com/api/heroes</code>).\nThis means that the URLs must be somehow converted to absolute when running on the server and be left relative when running in the browser.</p>\n<p>If you are using one of the <code>@nguniversal/*-engine</code> packages (such as <code>@nguniversal/express-engine</code>), this is taken care for you automatically.\nYou don't need to do anything to make relative URLs work on the server.</p>\n<p>If, for some reason, you are not using an <code>@nguniversal/*-engine</code> package, you may need to handle it yourself.</p>\n<p>The recommended solution is to pass the full request URL to the <code>options</code> argument of <a href=\"api/platform-server/renderModule\">renderModule()</a> or <a href=\"api/platform-server/renderModuleFactory\">renderModuleFactory()</a> (depending on what you use to render <code>AppServerModule</code> on the server).\nThis option is the least intrusive as it does not require any changes to the app.\nHere, \"request URL\" refers to the URL of the request as a response to which the app is being rendered on the server.\nFor example, if the client requested <code>https://my-server.com/dashboard</code> and you are rendering the app on the server to respond to that request, <code>options.url</code> should be set to <code>https://my-server.com/dashboard</code>.</p>\n<p>Now, on every HTTP request made as part of rendering the app on the server, Angular can correctly resolve the request URL to an absolute URL, using the provided <code>options.url</code>.</p>\n\n \n</div>\n\n<!-- links to this doc:\n - api/platform-server\n - api/router/ExtraOptions\n - api/router/InitialNavigation\n - guide/architecture-next-steps\n - guide/glossary\n - guide/ivy\n - guide/web-worker\n-->\n<!-- links from this doc:\n - api/animations/browser\n - api/common/APP_BASE_HREF\n - api/common/DOCUMENT\n - api/common/Location\n - api/common/http/HttpClient\n - api/core/NgModule\n - api/platform-server/renderModule\n - api/platform-server/renderModuleFactory\n - api/router/RouterLink\n - api/upgrade/static\n - guide/aot-compiler\n - guide/glossary#cli\n - guide/router\n - guide/router#router-link\n - guide/universal#facilitate-web-crawlers-seo\n - guide/universal#filtering-request-urls\n - guide/universal#improve-performance-on-mobile-and-low-powered-devices\n - guide/universal#server-side-rendering-ssr-with-angular-universal\n - guide/universal#serving-static-files-safely\n - guide/universal#show-the-first-page-quickly\n - guide/universal#universal-engine\n - guide/universal#universal-in-action\n - guide/universal#universal-template-engine\n - guide/universal#universal-tutorial\n - guide/universal#universal-web-servers\n - guide/universal#using-absolute-urls-for-http-data-requests-on-the-server\n - guide/universal#why-use-server-side-rendering\n - guide/universal#working-around-the-browser-apis\n - tutorial\n - http://localhost:4200/\n - https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#throttling\n - https://developers.google.com/web/tools/lighthouse/audits/first-contentful-paint\n - https://developers.google.com/web/updates/2019/02/rendering-on-the-web\n - https://expressjs.com/\n - https://github.com/angular/angular/edit/master/aio/content/guide/universal.md?message=docs%3A%20describe%20your%20change...\n - https://github.com/angular/preboot\n - https://github.com/angular/universal\n - https://static.googleusercontent.com/media/www.google.com/en//webmasters/docs/search-engine-optimization-starter-guide.pdf\n - https://web.dev/shopping-for-speed-on-ebay/\n-->"
|
|
} |