From 0ea5f8b5ed10f9621982c649044e8bf6836f46fa Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Fri, 6 Oct 2017 14:34:29 -0700 Subject: [PATCH] =?UTF-8?q?docs:=20add=20universal=20guide=20with=20produc?= =?UTF-8?q?tion=20client=20app=20-=20with=20JK=E2=80=99s=20edits=20(#18707?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR Close #18707 --- .../examples/universal/example-config.json | 3 + .../universal/src/universal/server.ts | 2 +- .../src/universal/universal-engine.ts | 17 +- .../universal/webpack.config.client.js | 2 +- .../universal/webpack.config.universal.js | 3 +- aio/content/guide/universal.md | 192 ++++++------------ 6 files changed, 70 insertions(+), 149 deletions(-) diff --git a/aio/content/examples/universal/example-config.json b/aio/content/examples/universal/example-config.json index e69de29bb2..61227cc07a 100644 --- a/aio/content/examples/universal/example-config.json +++ b/aio/content/examples/universal/example-config.json @@ -0,0 +1,3 @@ +{ + "projectType": "systemjs" +} diff --git a/aio/content/examples/universal/src/universal/server.ts b/aio/content/examples/universal/src/universal/server.ts index 92103a89cd..f6248c11ac 100644 --- a/aio/content/examples/universal/src/universal/server.ts +++ b/aio/content/examples/universal/src/universal/server.ts @@ -23,7 +23,7 @@ server.engine('html', universalEngine({ appModuleFactory: AppServerModuleNgFactory })); -// engine should find templates (like index-universal.html) in 'dist/' by default +// engine should find templates in 'dist/' by default server.set('views', 'dist'); // #enddocregion universal-engine diff --git a/aio/content/examples/universal/src/universal/universal-engine.ts b/aio/content/examples/universal/src/universal/universal-engine.ts index bab955b8de..8f0e506f7c 100644 --- a/aio/content/examples/universal/src/universal/universal-engine.ts +++ b/aio/content/examples/universal/src/universal/universal-engine.ts @@ -8,7 +8,6 @@ import { renderModuleFactory } from '@angular/platform-server'; import { APP_BASE_HREF } from '@angular/common'; const templateCache: { [key: string]: string } = {}; // page templates -const outputCache: { [key: string]: string } = {}; // rendered pages export function universalEngine(setupOptions: any) { @@ -21,15 +20,6 @@ export function universalEngine(setupOptions: any) { const { req } = options; const routeUrl = req.url; - const html = outputCache[routeUrl]; - if (html) { - // return already-built page for this url - console.log('from cache: ' + routeUrl); - callback(null, html); - return; - } - - console.log('building: ' + routeUrl); let template = templateCache[filePath]; if (!template) { template = fs.readFileSync(filePath).toString(); @@ -39,8 +29,8 @@ export function universalEngine(setupOptions: any) { const { appModuleFactory } = setupOptions; const origin = getOrigin(req); - // render the page // #docregion render + // render the page renderModuleFactory(appModuleFactory, { document: template, url: routeUrl, @@ -48,10 +38,7 @@ export function universalEngine(setupOptions: any) { { provide: APP_BASE_HREF, useValue: origin } ] }) - .then(page => { - outputCache[routeUrl] = page; - callback(null, page); - }); + .then(page => callback(null, page)); // #enddocregion render }; } diff --git a/aio/content/examples/universal/webpack.config.client.js b/aio/content/examples/universal/webpack.config.client.js index f924f7bcae..88c352359e 100644 --- a/aio/content/examples/universal/webpack.config.client.js +++ b/aio/content/examples/universal/webpack.config.client.js @@ -12,7 +12,7 @@ module.exports = { extensions: ['.ts', '.js'] }, output: { - path: 'dist', + path: __dirname + '/dist', filename: 'client.js' }, plugins: [ diff --git a/aio/content/examples/universal/webpack.config.universal.js b/aio/content/examples/universal/webpack.config.universal.js index b24943fc13..b74c71904c 100644 --- a/aio/content/examples/universal/webpack.config.universal.js +++ b/aio/content/examples/universal/webpack.config.universal.js @@ -16,7 +16,7 @@ module.exports = { }, target: 'node', output: { - path: 'dist', + path: __dirname + '/dist', filename: 'server.js' }, plugins: [ @@ -28,7 +28,6 @@ module.exports = { // copy assets to the output (/dist) folder new CopyWebpackPlugin([ {from: 'src/index-universal.html'}, - {from: 'src/favicon.ico'}, {from: 'src/styles.css'}, {from: 'node_modules/core-js/client/shim.min.js'}, {from: 'node_modules/zone.js/dist/zone.min.js'}, diff --git a/aio/content/guide/universal.md b/aio/content/guide/universal.md index 414e6bf667..7482570f79 100644 --- a/aio/content/guide/universal.md +++ b/aio/content/guide/universal.md @@ -2,6 +2,14 @@ This guide describes **Angular Universal**, a technology that runs your Angular application on the server. +
+ +This is a **preview guide**. +The Angular CLI is adding support for universal apps and +we will realign this guide with the CLI as soon as possible. + +
+ A normal Angular application executes in the _browser_, rendering pages in the DOM in response to user actions. **Angular Universal** generates _static_ application pages on the _server_ @@ -18,24 +26,8 @@ Meanwhile, the browser downloads the full client version and switches to it auto [Download the finished sample code](generated/zips/universal/universal.zip), which runs in a [node express](https://expressjs.com/) server. -Almost _any_ web server technology can serve a Universal app. -See this advanced example written for -[ASP.NET Core](https://github.com/MarkPieszak/aspnetcore-angular2-universal). -
- -The build setup described in this guide is experimental and subject to change. - -
- -## Overview - -This overview explains the benefits of a Universal application and how it works. Then it describes the sample application that goes with this guide. - -Subsequent sections describe a sample Universal application derived from the Tour of Heroes tutorial -and explain how to build and run that app. - {@a why-do-it} ### Why Universal @@ -50,7 +42,7 @@ There are three main reasons to create a Universal version of your app. {@a web-crawlers} #### Facilitate web crawlers -Google, Bing, Facebook, twitter and other social media sites rely on web crawlers to index your application content and make that content searchable on the web. +Google, Bing, Facebook, Twitter and other social media sites rely on web crawlers to index your application content and make that content searchable on the web. These web crawlers may be unable to navigate and index your highly-interactive, Angular application as a human user could do. @@ -75,10 +67,6 @@ people who otherwise would not be able to use the app at all. Displaying the first page quickly can be critical for user engagement. -Captive users of a line-of-business app may have to wait. -But a casual visitor will switch to a faster site if your app takes "too long" to show the first page. - -While [AOT](guide/aot-compiler) compilation speeds up application start times, it might not be fast enough for some of your audience, especially users on mobile devices with slow connections. [53% of mobile site visits are abandoned](https://www.doubleclickbygoogle.com/articles/mobile-speed-matters/) if pages take longer than 3 seconds to load. Your app may have to launch faster to engage these users before they decide to do something else. @@ -86,19 +74,11 @@ With Angular Universal, you can generate landing pages for the app that look lik The pages are pure HTML, and can display even if JavaScript is disabled. The pages do not handle browser events, but they _do_ support navigation through the site using [routerLink](guide/router.html#router-link). -Of course most Angular apps are highly interactive. -The landing page looks real and is far more useful than a "loading" spinner. -But it won't fool anyone for long. - In practice, you'll serve a static version of the landing page to hold the user's attention. At the same time, you'll load the full Angular app behind it in the manner [explained below](#transition). The user perceives near-instant performance from the landing page and gets the full interactive experience after the full app loads. -
-Another tool called Preboot can record browser events such as user keystrokes during the transition and play them back in the full Angular app once it is loaded. -
- {@a how-does-it-work} ### How it works @@ -124,7 +104,7 @@ Finally, the server returns the rendered page to the client. ### Working around the browser APIs -Because a Universal `platform-server` app doesn't execute in the browser, you may have to work around some of the APIs and capabilities that you otherwise take for granted on the client. +Because a Universal `platform-server` app doesn't execute in the browser, you may have to work around some of the browser APIs and capabilities that are missing on the server. You won't be able reference browser-only native objects such as `window`, `document`, `navigator` or `location`. If you don't need them on the server-rendered page, side-step them with conditional logic. @@ -137,11 +117,6 @@ Without mouse or keyboard events, a universal app can't rely on a user clicking A universal app should determine what to render based solely on the incoming client request. This is a good argument for making the app [routeable](guide/router). -Http requests with _relative_ URLs don't work. -You should convert them to _absolute_ URLs on the server which means you'll need to know the server origin. -You can pass the server origin into your app with a [provider](guide/dependency-injection#injector-providers) "universal/*" - as you'll see in the [example below](#http-urls). - Because the user of a server-rendered page can't do much more than click links, you should [swap in the real client app](#transition) as quickly as possible for a proper interactive experience. @@ -224,27 +199,19 @@ npm install @angular/compiler-cli @angular/platform-server express --save npm install webpack @ngtools/webpack copy-webpack-plugin raw-loader @types/express --save-dev -### Modify the client app - -You'll have to modify the client application in a few small ways to enable server-side rendering and -to facilitate the transition from the Universal app to the client app. - {@a transition} -#### Enable transition to the client app +### Modify the client app -A Universal app can act as a dynamic "splash screen" that shows a view of your app while the real client app loads behind it. -This gives the appearance of a near-instant application. +A Universal app can act as a dynamic, content-rich "splash screen" that engages the user. +It gives the appearance of a near-instant application. Meanwhile, the browser downloads the client app scripts in background. -Once loaded, Angular transitions from the static server-rendered page to the dynamically rendered views of the live client app. +Once loaded, Angular transitions from the static server-rendered page to the dynamically rendered views of the interactive client app. -To make this work, the template for server-side rendering contains the `