549 lines
22 KiB
Markdown
549 lines
22 KiB
Markdown
@title
|
|
Deployment
|
|
|
|
@intro
|
|
Learn how to deploy your Angular app.
|
|
|
|
@description
|
|
This page describes tools and techniques for deploy and optimize your Angular application.
|
|
|
|
|
|
{@a toc}
|
|
## Table of contents
|
|
* [Overview](#overview)
|
|
* [Simplest deployment possible](#dev-deploy)
|
|
* [Optimize for production](#optimize)
|
|
* [Ahead-of-Time (AOT) compilation](#aot)
|
|
* [Webpack](#webpack)
|
|
* [Tree shaking with _rollup_](#rollup)
|
|
* [Pruned libraries](#prune)
|
|
* [Measure performance first](#measure)
|
|
* [Angular configuration](#angular-configuration)
|
|
* [The `base` tag](#base-tag)
|
|
* [Enable production mode](#enable-prod-mode)
|
|
* [Lazy loading](#lazy-loading)
|
|
* [Server configuration](#server-configuration)
|
|
* [Routed apps must fallback to `index.html`](#fallback)
|
|
* [CORS: requesting services from a different server](#cors)
|
|
|
|
|
|
{@a overview}
|
|
|
|
## Overview
|
|
|
|
This guide describes techniques for preparing and deploying an Angular application to a server running remotely.
|
|
The techniques progress from _easy but suboptimal_ to _more optimal and more involved_.
|
|
|
|
* The [simple way](#dev-deploy "Simplest deployment possible") is to copy the development environment to the server.
|
|
|
|
* [_Ahead of Time_ compilation (AOT)](#aot "AOT Compilation") is the first of
|
|
[several optimization strategies](#optimize).
|
|
You'll also want to read the [detailed instructions in the AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook").
|
|
|
|
* [Webpack](#webpack "Webpack Optimization") is a popular general purpose packaging tool with a rich ecosystem, including plugins for AOT.
|
|
The Angular [webpack guide](webpack.html "Webpack: an introduction") can get you started and
|
|
_this_ page provides additional optimization advice, but you'll probably have to learn more about webpack on your own.
|
|
|
|
* The [Angular configuration](#angular-configuration "Angular configuration") section calls attention to
|
|
specific client application changes that could improve performance.
|
|
|
|
* The [Server configuration](#server-configuration "Server configuration") section describes
|
|
server-side changes that may be necessary, _no matter how you deploy the application_.
|
|
|
|
|
|
|
|
{@a dev-deploy}
|
|
## Simplest deployment possible
|
|
|
|
The simplest way to deploy the app is to publish it to a web server
|
|
directly out of the development environment.
|
|
|
|
It's already running locally. You'll just copy it, almost _as is_,
|
|
to a non-local server that others can reach.
|
|
|
|
1. Copy _everything_ (or [_almost_ everything](#node-modules "Loading npm packages from the web"))
|
|
from the local project folder to a folder on the server.
|
|
|
|
1. If you're serving the app out of a subfolder,
|
|
edit a version of `index.html` to set the `<base href>` appropriately.
|
|
For example, if the URL to `index.html` is `www.mysite.com/my/app/`, set the _base href_ to
|
|
`<base href="/my/app/">`.
|
|
Otherwise, leave it alone.
|
|
[More on this below](#base-tag).
|
|
|
|
1. Configure the server to redirect requests for missing files to `index.html`.
|
|
[More on this below](#fallback).
|
|
|
|
1. Enable production mode as [described below](#enable-prod-mode) (optional).
|
|
|
|
That's the simplest deployment you can do.
|
|
|
|
|
|
~~~ {.alert.is-helpful}
|
|
|
|
This is _not_ a production deployment. It's not optimized and it won't be fast for users.
|
|
It might be good enough for sharing your progress and ideas internally with managers, teammates, and other stakeholders.
|
|
Be sure to read about [optimizing for production](#optimize "Optimizing for production") below.
|
|
|
|
|
|
|
|
~~~
|
|
|
|
|
|
|
|
{@a node-modules}
|
|
### Load npm package files from the web (SystemJS)
|
|
|
|
The `node_modules` folder of _npm packages_ contains much more code
|
|
than is needed to actually run your app in the browser.
|
|
The `node_modules` for the Quickstart installation is typically 20,500+ files and 180+ MB.
|
|
The application itself requires a tiny fraction of that to run.
|
|
|
|
It takes a long time to upload all of that useless bulk and
|
|
users will wait unnecessarily while library files download piecemeal.
|
|
|
|
Load the few files you need from the web instead.
|
|
|
|
(1) Make a copy of `index.html` for deployment and replace all `node_module` scripts
|
|
with versions that load from the web. It might look like this.
|
|
|
|
|
|
{@example 'deployment/ts/src/index.html' region='node-module-scripts'}
|
|
|
|
(2) Replace the `systemjs.config.js` script with a script that
|
|
loads `systemjs.config.server.js`.
|
|
|
|
{@example 'deployment/ts/src/index.html' region='systemjs-config'}
|
|
|
|
(3) Add `systemjs.config.server.js` (shown in the code sample below) to the `src/` folder.
|
|
This alternative version configures _SystemJS_ to load _UMD_ versions of Angular
|
|
(and other third-party packages) from the web.
|
|
|
|
Modify `systemjs.config.server.js` as necessary to stay in sync with changes
|
|
you make to `systemjs.config.js`.
|
|
|
|
Notice the `paths` key:
|
|
|
|
|
|
{@example 'deployment/ts/src/systemjs.config.server.js' region='paths'}
|
|
|
|
In the standard SystemJS config, the `npm` path points to the `node_modules/`.
|
|
In this server config, it points to
|
|
<a href="https://unpkg.com/" target="_blank" title="unpkg.com">https://unpkg.com</a>,
|
|
a site that hosts _npm packages_,
|
|
and loads them from the web directly.
|
|
There are other service providers that do the same thing.
|
|
|
|
If you are unwilling or unable to load packages from the open web,
|
|
the inventory in `systemjs.config.server.js` identifies the files and folders that
|
|
you would copy to a library folder on the server.
|
|
Then change the config's `'npm'` path to point to that folder.
|
|
|
|
### Practice with an example
|
|
|
|
The following trivial router sample app shows these changes.
|
|
|
|
<md-tab-group>
|
|
|
|
<md-tab label="index.html">
|
|
{@example 'deployment/ts/src/index.html'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="systemjs.config.server.js">
|
|
{@example 'deployment/ts/src/systemjs.config.server.js'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="main.ts">
|
|
{@example 'deployment/ts/src/main.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="app/app.module.ts">
|
|
{@example 'deployment/ts/src/app/app.module.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="app/app.component.ts">
|
|
{@example 'deployment/ts/src/app/app.component.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="app/crisis-list.component.ts">
|
|
{@example 'deployment/ts/src/app/crisis-list.component.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="app/hero-list.component.ts">
|
|
{@example 'deployment/ts/src/app/hero-list.component.ts'}
|
|
</md-tab>
|
|
|
|
|
|
</md-tab-group>
|
|
|
|
Practice with this sample before attempting these techniques on your application.
|
|
|
|
1. Follow the [setup instructions](../guide/setup.html "Angular QuickStart setup") for creating a new project
|
|
named <ngio-ex path="simple-deployment"></ngio-ex>.
|
|
|
|
1. Add the "Simple deployment" sample files shown above.
|
|
|
|
1. Run it with `npm start` as you would any project.
|
|
|
|
1. Inspect the network traffic in the browser developer tools.
|
|
Notice that it loads all packages from the web.
|
|
You could delete the `node_modules` folder and the app would still run
|
|
(although you wouldn't be able to recompile or launch `lite-server`
|
|
until you restored it).
|
|
|
|
1. Deploy the sample to the server (minus the `node_modules` folder!).
|
|
|
|
When you have that working, try the same process on your application.
|
|
|
|
|
|
{@a optimize}
|
|
|
|
## Optimize for production
|
|
|
|
Although deploying directly from the development environment works, it's far from optimal.
|
|
|
|
The client makes many small requests for individual application code and template files,
|
|
a fact you can quickly confirm by looking at the network tab in a browser's developer tools.
|
|
Each small file download can spend more time communicating with the server than tranfering data.
|
|
|
|
Development files are full of comments and whitespace for easy reading and debugging.
|
|
The browser downloads entire libraries, instead of just the parts the app needs.
|
|
The volume of code passed from server to client (the "payload")
|
|
can be significantly larger than is strictly necessary to execute the application.
|
|
|
|
The many requests and large payloads mean
|
|
the app takes longer to launch than it would if you optimized it.
|
|
Several seconds may pass (or worse) before the user can see or do anything userful.
|
|
|
|
Does it matter? That depends upon business and technical factors you must evaluate for yourself.
|
|
|
|
If it _does_ matter, there are tools and techniques to reduce the number of requests and the size of responses.
|
|
|
|
- Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates.
|
|
- Bundling: concatenates modules into a single file (bundle).
|
|
- Inlining: pulls template html and css into the components.
|
|
- Minification: removes excess whitespace, comments, and optional tokens.
|
|
- Uglification: rewrites code to use short, cryptic variable and function names.
|
|
- Dead code elimination: removes unreferenced modules and unused code.
|
|
- Pruned libraries: drop unused libraries and pare others down to the features you need.
|
|
- Performance measurement: focus on optimizations that make a measurable difference.
|
|
|
|
Each tool does something different.
|
|
They work best in combination and are mutually reinforcing.
|
|
|
|
You can use any build system you like.
|
|
Whatever system you choose, be sure to automate it so that
|
|
building for production is a single step.
|
|
|
|
|
|
{@a aot}
|
|
### Ahead-of-Time (AOT) compilation
|
|
|
|
The Angular _Ahead-of-Time_ compiler pre-compiles application components and their templates
|
|
during the build process.
|
|
|
|
Apps compiled with AOT launch faster for several reasons.
|
|
* Application components execute immediately, without client-side compilation.
|
|
* Templates are embedded as code within their components so there is no client-side request for template files.
|
|
* You don't download the Angular compiler, which is pretty big on its own.
|
|
* The compiler discards unused Angular directives that a tree-shaking tool can then exclude.
|
|
|
|
Learn more about AOT Compilation in the [AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook")
|
|
which describes running the AOT compiler from the command line
|
|
and using [_rollup_](#rollup) for bundling, minification, uglification and tree shaking.
|
|
|
|
|
|
{@a webpack}
|
|
### Webpack (and AOT)
|
|
|
|
<a href="https://webpack.js.org/" target="_blank" title="Webpack 2">Webpack 2</a> is another
|
|
great option for inlining templates and style-sheets, for bundling, minifying, and uglifying the application.
|
|
The "[Webpack: an introduction](webpack.html "Webpack: an introduction")" guide will get you started
|
|
using webpack with Angular.
|
|
|
|
Consider configuring _Webpack_ with the official
|
|
<a href="https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack" target="_blank" title="Ahead-of-Time Webpack Plugin">
|
|
Angular Ahead-of-Time Webpack Plugin</a>.
|
|
This plugin transpiles the TypeScript application code,
|
|
bundles lazy loaded `NgModules` separately,
|
|
and performs AOT compilation — without any changes to the source code.
|
|
|
|
|
|
{@a rollup}
|
|
### Dead code elimination with _rollup_
|
|
|
|
Any code that you don't call is _dead code_.
|
|
You can reduce the total size of the application substantially by removing dead code from the application and from third-party libraries.
|
|
|
|
_Tree shaking_ is a _dead code elimination_ technique that removes entire exports from JavaScript modules.
|
|
If a library exports something that the application doesn't import, a tree shaking tool removes it from the code base.
|
|
|
|
Tree shaking was popularized by
|
|
<a href="http://rollupjs.org/" target="_blank" title="Rollup">Rollup</a>, a popular tool with an ecosystem of
|
|
plugins for bundling, minification, and uglification.
|
|
Learn more about tree shaking and dead code elmination in
|
|
<a href="https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80#.15ih9cyvl" target="_blank" title="Tree-shaking and Dead Code Elimination">
|
|
this post</a> by rollup-creator, Rich Harris.
|
|
|
|
|
|
{@a prune}
|
|
### Pruned libraries
|
|
|
|
Don't count on automation to remove all dead code.
|
|
|
|
Remove libraries that you don't use, especially unnecessary scripts in `index.html`.
|
|
Consider smaller alternatives to the libraries that you do use.
|
|
|
|
Some libraries offer facilities for building a custom, skinny version with just the features you need.
|
|
Other libraries let you import features _a la carte_.
|
|
**RxJS** is a good example; import RxJS `Observable` operators individually instead of the entire library.
|
|
|
|
|
|
{@a measure}
|
|
### Measure performance first
|
|
|
|
You can make better decisions about what to optimize and how when you have a clear and accurate understanding of
|
|
what's making the application slow.
|
|
The cause may not be what you think it is.
|
|
You can waste a lot of time and money optimizing something that has no tangible benefit or even makes the app slower.
|
|
You should measure the app's actual behavior when running in the environments that are important to you.
|
|
|
|
The
|
|
<a href="https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing" target="_blank" title="Chrome DevTools Network Performance">
|
|
Chrome DevTools Network Performance page</a> is a good place to start learning about measuring performance.
|
|
|
|
The [WebPageTest](https://www.webpagetest.org/) tool is another good choice
|
|
that can also help verify that your deployment was successful.
|
|
|
|
|
|
{@a angular-configuration}
|
|
|
|
## Angular configuration
|
|
|
|
Angular configuration can make the difference between whether the app launches quickly or doesn't load at all.
|
|
|
|
|
|
{@a base-tag}
|
|
### The `base` tag
|
|
|
|
The HTML [_<base href="..."/>_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)
|
|
specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets.
|
|
For example, given the `<base href="/my/app/">`, the browser resolves a URL such as `some/place/foo.jpg`
|
|
into a server request for `my/app/some/place/foo.jpg`.
|
|
During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files.
|
|
|
|
See also the [*APP_BASE_HREF*](../api/common/index/APP_BASE_HREF-let.html "API: APP_BASE_HREF") alternative.In development, you typically start the server in the folder that holds `index.html`.
|
|
That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app.
|
|
|
|
But on the shared or production server, you might serve the app from a subfolder.
|
|
For example, when the URL to load the app is something like `http://www.mysite.com/mysrc/app/`,
|
|
the subfolder is `my/app/` and you should add `<base href="/my/app/">` to the server version of the `index.html`.
|
|
|
|
When the `base` tag is misconfigured, the app fails to load and the browser console displays `404 - Not Found` errors
|
|
for the missing files. Look at where it _tried_ to find those files and adjust the base tag appropriately.
|
|
|
|
|
|
{@a enable-prod-mode}
|
|
### Enable production mode
|
|
|
|
Angular apps run in development mode by default, as you can see by the following message on the browser
|
|
console:
|
|
|
|
<code-example format="nocode">
|
|
Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.
|
|
</code-example>
|
|
|
|
Switching to production mode can make it run faster by disabling development specific checks such as the dual change detection cycles.
|
|
|
|
To enable [production mode](../api/core/index/enableProdMode-function.html) when running remotely, add the following code to the `main.ts`.
|
|
|
|
|
|
{@example 'deployment/ts/src/main.ts' region='enableProdMode'}
|
|
|
|
|
|
|
|
{@a lazy-loading}
|
|
### Lazy loading
|
|
|
|
You can dramatically reduce launch time by only loading the application modules that
|
|
absolutely must be present when the app starts.
|
|
|
|
Configure the Angular Router to defer loading of all other modules (and their associated code), either by
|
|
[waiting until the app has launched](router.html#preloading "Preloading")
|
|
or by [_lazy loading_](router.html#asynchronous-routing "Lazy loading")
|
|
them on demand.
|
|
|
|
#### Don't eagerly import something from a lazy loaded module
|
|
|
|
It's a common mistake.
|
|
You've arranged to lazy load a module.
|
|
But you unintentionally import it, with a JavaScript `import` statement,
|
|
in a file that's eagerly loaded when the app starts, a file such as the root `AppModule`.
|
|
If you do that, the module will be loaded immediately.
|
|
|
|
The bundling configuration must take lazy loading into consideration.
|
|
Because lazy loaded modules aren't imported in JavaScript (as just noted), bundlers exclude them by default.
|
|
Bundlers don't know about the router configuration and won't create separate bundles for lazy loaded modules.
|
|
You have to create these bundles manually.
|
|
|
|
The
|
|
[Angular Ahead-of-Time Webpack Plugin](https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack)
|
|
automatically recognizes lazy loaded `NgModules` and creates separate bundles for them.
|
|
|
|
|
|
|
|
{@a server-configuration}
|
|
|
|
## Server configuration
|
|
|
|
This section covers changes you may have make to the server or to files deployed to the server.
|
|
|
|
|
|
{@a fallback}
|
|
### Routed apps must fallback to `index.html`
|
|
|
|
Angular apps are perfect candidates for serving with a simple static HTML server.
|
|
You don't need a server-side engine to dynamically compose application pages because
|
|
Angular does that on the client-side.
|
|
|
|
If the app uses the Angular router, you must configure the server
|
|
to return the application's host page (`index.html`) when asked for a file that it does not have.
|
|
|
|
|
|
{@a deep-link}
|
|
A routed application should support "deep links".
|
|
A _deep link_ is a URL that specifies a path to a component inside the app.
|
|
For example, `http://www.mysite.com/heroes/42` is a _deep link_ to the hero detail page
|
|
that displays the hero with `id: 42`.
|
|
|
|
There is no issue when the user navigates to that URL from within a running client.
|
|
The Angular router interprets the URL and routes to that page and hero.
|
|
|
|
But clicking a link in an email, entering it in the browser address bar,
|
|
or merely refreshing the browser while on the hero detail page —
|
|
all of these actions are handled by the browser itself, _outside_ the running application.
|
|
The browser makes a direct request to the server for that URL, bypassing the router.
|
|
|
|
A static server routinely returns `index.html` when it receives a request for `http://www.mysite.com/`.
|
|
But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found` error *unless* it is
|
|
configured to return `index.html` instead.
|
|
|
|
#### Fallback configuration examples
|
|
|
|
There is no single configuration that works for every server.
|
|
The following sections describe configurations for some of the most popular servers.
|
|
The list is by no means exhaustive, but should provide you with a good starting point.
|
|
|
|
#### Development servers
|
|
|
|
- [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
|
|
[Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`.
|
|
|
|
- [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
|
|
`historyApiFallback` entry in the dev server options as follows:
|
|
|
|
<code-example>
|
|
historyApiFallback: {
|
|
disableDotRule: true,
|
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
|
|
}
|
|
|
|
</code-example>
|
|
|
|
#### Production servers
|
|
|
|
- [Apache](https://httpd.apache.org/): add a
|
|
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html)
|
|
to the `.htaccess` file as show
|
|
[here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
|
|
<code-example format=".">
|
|
RewriteEngine On
|
|
# If an existing asset or directory is requested go to it as it is
|
|
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
|
|
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
|
|
RewriteRule ^ - [L]
|
|
|
|
# If the requested resource doesn't exist, use index.html
|
|
RewriteRule ^ /index.html
|
|
|
|
</code-example>
|
|
|
|
- [NGinx](http://nginx.org/): use `try_files`, as described in
|
|
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
|
|
modified to serve `index.html`:
|
|
|
|
<code-example format=".">
|
|
try_files $uri $uri/ /index.html;
|
|
|
|
</code-example>
|
|
|
|
- [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
|
|
[here](http://stackoverflow.com/a/26152011/2116927):
|
|
<code-example format="." escape="html">
|
|
<system.webServer>
|
|
<rewrite>
|
|
<rules>
|
|
<rule name="Angular Routes" stopProcessing="true">
|
|
<match url=".*" />
|
|
<conditions logicalGrouping="MatchAll">
|
|
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
|
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
|
</conditions>
|
|
<action type="Rewrite" url="/" />
|
|
</rule>
|
|
</rules>
|
|
</rewrite>
|
|
</system.webServer>
|
|
|
|
</code-example>
|
|
|
|
- [GitHub Pages](https://pages.github.com/): you can't
|
|
[directly configure](https://github.com/isaacs/github/issues/408)
|
|
the GitHub Pages server, but you can add a 404 page.
|
|
Copy `index.html` into `404.html`.
|
|
It will still be served as the 404 response, but the browser will process that page and load the app properly.
|
|
It's also a good idea to
|
|
[serve from `docs/` on master](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch)
|
|
and to
|
|
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
|
|
|
|
- [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
|
|
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
|
|
|
|
<code-example format=".">
|
|
"rewrites": [ {
|
|
"source": "**",
|
|
"destination": "/index.html"
|
|
} ]
|
|
|
|
</code-example>
|
|
|
|
|
|
|
|
{@a cors}
|
|
|
|
### Requesting services from a different server (CORS)
|
|
|
|
Angular developers may encounter a
|
|
<a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing" target="_blank" title="Cross-origin resource sharing">
|
|
<i>cross-origin resource sharing</i></a> error when making a service request (typically a data service request).
|
|
to a server other than the application's own host server.
|
|
Browsers forbid such requests unless the server permits them explicitly.
|
|
|
|
There isn't anything the client application can do about these errors.
|
|
The server must be configured to accept the application's requests.
|
|
Read about how to enable CORS for specific servers at
|
|
<a href="http://enable-cors.org/server.html" target="_blank" title="Enabling CORS server">enable-cors.org</a>.
|
|
|
|
|
|
{@a next-steps}
|
|
|
|
## Next steps
|
|
If you want to go beyond the [simple _copy-deploy_](#dev-deploy "Simplest deployment possible") approach,
|
|
read the [AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook") next. |