Skip to content
Server side rendering using Angular Ivy
TypeScript Python JavaScript CSS HTML
Branch: master
Clone or download
Latest commit fc15f51 Jul 24, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
e2e
scripts change to a dark theme Apr 6, 2019
src cleanup tsconfig.app.json output path Jun 4, 2019
.bazelignore add bazelignore of node_modules Mar 7, 2019
.bazelrc Turn off bazel sandboxing for now Mar 7, 2019
.editorconfig initial commit Dec 12, 2018
.gitignore Introduce Bazel Feb 15, 2019
BAZEL.md Add kubernetes deployment Mar 7, 2019
BUILD.bazel Add kubernetes deployment Mar 7, 2019
README.md Update README.md Jul 24, 2019
TODO add support for fetching initial data in a page/component. Mar 18, 2019
WORKSPACE more fixes for bazel build. Mar 26, 2019
angular.json
deployment.yaml Add kubernetes deployment Mar 7, 2019
package-lock.json
package.json cleanup npm actions and update README.md Apr 6, 2019
rollup.config.js cleanup tsconfig.app.json output path Jun 4, 2019
tsconfig.json add rollup config for producing small ESM bundes Mar 7, 2019
tslint.json initial commit Dec 12, 2018

README.md

Ivy

This project was generated with Angular CLI version 7.1.1.

This is an initial test of server side rendering on top of Angular Ivy.

The base Ivy client project was generated by

ng new ivy --experimental-ivy

Build and run the Universal application

Installation

npm ci

(To get the exactly pinned version in package-lock.json)

Running in prod mode (to see the actual JS payload sizes)

npm run start

Navigate to http://localhost:4200

Running in dev mode (for development)

npm run dev

Navigate to http://localhost:4200

The page will live reload when code is changed.

What's this?

The example illustrates how Angular Ivy makes the following possible

  1. "Automatic" code splitting at component and route level
  2. Server-side Rendering(SSR) with Angular Ivy
  3. Selective client side rehydration based on user events / data binding changes

Coming soon:

  1. Configurable load and prefetch strategies
  2. Data fetching and state transfer
  3. Server streaming?

Code organization

All the things happening server side are at https://github.com/vikerman/ivy-universal/tree/master/server All the user code are in https://github.com/vikerman/ivy-universal/tree/master/src/ except for https://github.com/vikerman/ivy-universal/tree/master/src/lib which contains all the supporting code which would eventually be part of some NPM node module.

The project itself relies on nightly Ivy builds with no modifications.

(Though main.ts, main.server.ts and routes.ts are currently hand written, they would eventually be automatically generated by Angular CLI using Angular architect APIs by parsing the decorator metadata for each component. This would be the part that would make the code splitting and progressive bootstrapping "automatic".)

What's happening and how does it work?

The webpack magic comments in https://github.com/vikerman/ivy-universal/blob/master/src/main.ts ensures that each Angular Component in the components/ and pages/ folder gets a separate chunk. The default Angular CLI webpack settings ensures that common chunks are automatcially created for any code shared between these components.

On the server the page for the current route gets rendered(more on this later). The rendered HTML is sent to the client. On the client the only code that runs is a bootstrapping code. This code does the following:

  • Setup a global event handler on the document in order to buffer events.
  • Registers a shell Custom Element for every known component in the system. This does not load the actual component code. It just uses the component metadata to watch for property binding changes.

At this point no Angular specific code or component code has been loaded. (Current bootstrap size without polyfills: ~12 Kb)

When a user initiates an event (say click a button) - The global event handler walks up the DOM tree from the event target till it find what appears to be a component root. It then uses webpack loader to load the chunk corresponding to the component.

The component on bootstrapping rehydrates the DOM node sent by the server using the rehydration renderer. This lets the component become active without having to load the code for its child components because as of now they weren't required to be re-rendered.

Let's say the currently active component changes its state based on the click handler and that resulted in chaging the data binding for the child - The shell Custom Element setup for the child component recognizes that its input properties have changed. In reaction to this the child Custom Element fetches the chunk coresponding to its component. The child component in turn boots up by rehydrating on top its server generated nodes and the process continues.

This way we can have a progressive bootstrapping strategy that loads components only on demand.

This is just an illustration of one possible strategy and how rehydration of SSR nodes is useful - but with the current organization of components as individually loadable units we can now tune the load strategy that makes sense for the application. In the future configurable prefetch and load strategies will be added to the project.

You can’t perform that action at this time.