Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
Universal Angular 2In Action
@GF SECURITIES
Github :
Weibo : _gc
Zhihu :
Email : [email protected]
@lightningtgc
@
@
Speaker/Writer/Globetrotter
-- Front End Expert @ GF.S-- Ex Tencenter
A What
What 's universal
what's the purpose
How
How to startHow it works
More More Universal
In FinTech
g e n ad
What
What happens
when you type
www.google.com
in your browserClient Server
Three Periods
client
Get Page
Static HTML
server(CDN/CGI)
Get CSS Files (or inline)
CSS (page parse)
Get JavaScript Files
JS (events, xhr)Request Data
Data
RenderTemplate
Period I
* Picture by Wassim Chegham
Get Page
Dynamic HTML(with data in tag...)
server(CDN/CGI)
Data fetch byserver
RenderTemplate
Period II
Async Get Assets(css, js)
Assets(parse, events)client
- DNS, TCP,RTT
Get Page
Dynamic HTML(with rendered templ) server(CDN/CGI)
Async get assets(css, js)
Assets(parse, events)
Data fetch byserver
Interactionor re-render
Period III server-side rendering
RenderTemplate
client
MoreOptimize?
Node.js
Front End Community
renderToString
2.0
Backbone
FastBoot
WAKE UPAngular Universal is coming!!!
There are only two hard thingsin Computer Science:
Cache invalidation
Naming things
-- Phil Karlton
not the samebut only look the same
2011: 2013: 2015:
isomorphic-javascriptAirBnb IsomorphicUniversal Javascript
Full Stack Front End:
Back End:
Angular 2 (DI, Router...)TypeScriptWebpack, system.js...Sass, postcss...rx.js (Observable)zone.js (async)
Node.js (Express, Hapi)Asp.netpreboot.jsParse5...
Angular 2
Github:lightningtgc/
awesome-ng2
Get Page
HTML(pre-render+preboot.js(inline))
server(CDN)Async Get Assets(css, js)
PrebootJS records events
Angular 2 Bootstraping
clientView rendered to hidden div
PrebootJS replays events
PrebootJS switches view context topreviously hidden div (clientView)
client
< 1s interaction
What'sthe purpose?
(Why?)
< 1s
Initial Load
APP take overServer rendered view
became visible
Without server-side rendering
With server-side rendering
SEO
Initial Load
Social Link
Legacy Browers
YES
YES
YES
YES
NO
NO
NO
Angular 2UniversalAngular 2
SPA
NO
HTML Mailor non-js
FB, TwitterLink Preview
Lazy LodingPerformance
How
How ToStart?
At First
Projects Relations:
universal-starter
universal
preboot
angular 2
parse5 Express(Hapi)
Engine
gulp(webpack)
Prerender
modules
(Express.js + Webpack)Universal Starter Kitgit clone https://github.com/angular/universal-starter.git
npm install
npm start
How itworks?
Nodenpm start
server.ts
main.node.ts
index.html
client.ts
main.browser.ts
Expressengine
Browsersend
app.component.ts
Server Line Browser Line
Show me the code?
Server Line
{ "name": "universal-starter", "version": "2.0.0", "scripts": { "start": "npm run server",
"prestart": "npm run build", "prebuild": "rimraf dist", "build": "webpack",
"server": "nodemon dist/server/index.js" }}
package.json (mini version)
> npm start
import * as express from 'express';const app = express();// Angular 2 Universalimport { expressEngine } from 'angular2-universal';
// Express Viewapp.engine('.html', expressEngine);app.set('views', __dirname);app.set('view engine', 'html');
import { ngApp } from './main.node';// ensure routes match client-side-appapp.get('/', ngApp);app.get('/home', ngApp);
// Serverlet server = app.listen(process.env.PORT || 3000, () => { console.log(`See: localhost:${server.address().port}`);});
server.ts (mini version)
import { REQUEST_URL,ORIGIN_URL,NODE_LOCATION_PROVIDERS, NODE_HTTP_PROVIDERS,ExpressEngineConfig} from 'angular2-universal';import { provideRouter } from '@angular/router';import { APP_BASE_HREF } from '@angular/common';// Applicationimport {App} from './app/app.component';import {routes} from './app/app.routes';
export function ngApp(req, res) { let baseUrl = '/'; let url = req.originalUrl || '/'; let config: ExpressEngineConfig = { directives: [ App ], platformProviders: [ {provide: ORIGIN_URL, useValue: 'http://localhost:3000'}, {provide: APP_BASE_HREF, useValue: baseUrl}, ], providers: [ {provide: REQUEST_URL, useValue: url}, NODE_HTTP_PROVIDERS,NODE_LOCATION_PROVIDERS provideRouter(routes) ], async: true, preboot: true }; res.render('index', config);}
main.node.ts
Async
Gap Event
@Component({ selector: 'app', // <app></app> directives: [ ...ROUTER_DIRECTIVES, XLarge ], styles: [` * { padding:0; margin:0; }`], template: `<span x-large>Hello {{ title }}!</span>`})export class App { title: string = 'Tang Guichuan'; data = {}; server: string;
constructor(public http: Http) { }
ngOnInit() { setTimeout(() => { this.server = 'This was rendered from the server!'; }, 10); // use services for http calls this.http.get('/data.json') .subscribe(res => { this.data = res.json(); }); }}
app.component.ts ( just Angular 2 )
Preboot.js1.preboot.complete()& replayEvent
3.getInlineCode to insertinto the server view.
2.prebootstrap.toString()& recordEvent
export function createPrebootHTML(code: string, config?: any): string { let html = '';
html += ` <style> ${ getPrebootCSS(config && config.uglify) } </style> `;
html += ` <script> ${ code } </script> `;
if (config && config.start === true) { html += '<script>\n preboot.start(); \n</script>'; }
return html;}
universal/src/node/ng_preboot.ts
<!doctype html><html lang="en"><head> <title>Angular 2 Universal Starter</title> <meta charset="UTF-8"> <meta name="description" content="Angular 2 Universal"> <link rel="icon" href="data:;base64,iVBORw0KGgo="> <base href="/"></head><body> <app> Loading Universal ... </app> <!-- In server.ts: app.use(express.static( path.join(ROOT,'dist/client'), {index: false} )); --> <script src="/index.js"></script></body></html>
index.html
<!DOCTYPE html><html lang="en"><head> <title>Angular 2 Universal Starter</title> <meta charset="UTF-8"> <meta name="description" content="Angular 2 Universal"> <link rel="icon" href="data:;base64,iVBORw0KGgo="> <base href="/">
<style>*[_ngcontent-yyj-1] { padding:0; margin:0; } #universal[_ngcontent-yyj-1] { text-align:center; font-weight:bold; padding:15px 0; }}</style></head><body> <app _nghost-yyj-1=""> <h3 _ngcontent-yyj-1="" id="universal">Angular2 Universal</h3> </app>
<style> .preboot-overlay{background:grey;opacity:.27}@keyframes spin{to{transform:rotate(1turn)}}.preboot-spinner </style> <script> !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if( </script>
<script> preboot.start(); </script>
<script src="/index.js"></script>
</body></html>
index.html (with prebootJS)
import {prebootComplete} from 'angular2-universal';import {ngApp} from './main.browser';
// on document ready bootstrap Angular 2document.addEventListener('DOMContentLoaded', () => { ngApp() .then(prebootComplete);});
client.ts Browser Line
import {bootstrap} from '@angular/platform-browser-dynamic';import { provideRouter } from '@angular/router';import { HTTP_PROVIDERS } from '@angular/http';
// Applicationimport {App} from './app/app.component';import {routes} from './app/app.routes';
// you must return bootstrap for client.tsexport function ngApp() { return bootstrap(App, [ ...HTTP_PROVIDERS, provideRouter(routes) ]);}
main.browser.ts
export function prebootComplete(value?: any) { if ('preboot' in window && !prebootCompleted) { (<any>window).preboot.complete(); } return value;}
universal/src/browser/bootstrap.ts
return { complete: complete, completeApp: completeApp, replayEvent: replayEvent, switchBuffer: switchBuffer, cleanup: cleanup, setFocus: setFocus, findClientNode: findClientNode, getNodeKey: getNodeKey };
preboot/src/browser/preboot_browser.ts
< 1s interaction
UniversalMechanism
Angular 1.xSSR?
DOM, jqLite
Adapter Pattern
Angular 1.x SSR in Github:
runvnc/angular-on-servera-lucas/angular.js-server
...
Application(Components...)
Renderer(createElement, Event handlers..)
DomAdapter(Abstract DOM API)
DomRenderer
Native
Web Worker...
ServerDomRenderer
Browser Node.js (Server View)
Parse5DomAdapterBrowserDomAdapter
Document pase5
extend
implement
implement
u s e
u s e
Bootloader(bootstrap)
Rendering
Engine(express...)
configure
Abstract UI Layer Platform Layer Runtime Layer
white: application
yellow: angular2
purple: execution environment
blue: angular-universal
Cross platform
"Nothingwas achieved
in the comfort zone."
More
More
Universal
Pre-rendering
At Build Timeimport * as gulp from 'gulp';import {gulpPrerender} from '@angular/universal';import {App} from './app';
gulp.task("prerender", () => { return gulp.src(['index.html']) .pipe(gulpPrerender({ directives: [App] }));});
Killer feature
DEPENDENCY
INJECTION
Use localStorage?
Same Interface,
Different implement,
Environmental Identity
More
Back End
Universal Support :
Node.js ( Express, hapi)Asp.net
Plan:
JavaPHP(Best Lang?)Node: KoaJS 1 ( 2)
More
Optimize
Front End Optimization
( Without localstorage)
node-cache
Call HTTP request twice?
Lazy loading (Chunk): Webpack Code Splitting
Others?
BigPipe: Pipelining web pages for high performance
Component Level Cache
BestPractice
/////////////////////////// ** Example Directive// Notice we don't touch the Element directly
@Directive({ selector: '[x-large]'})export class XLarge { constructor(element: ElementRef, renderer: Renderer) { // ** IMPORTANT ** // we must interact with the dom through -Renderer- // for webworker/server to see the changes renderer.setElementStyle(element.nativeElement, 'fontSize', 'x-large'); // ^^ }}
app.component.ts
DO NOT USE Global Namespace such as navigator ordocument from browser.
InFinTech
More and More?