Angular Universal on Google App Engine

267b5cb132e543e4bbac780632fd4742?s=47 Kenji Imamula
November 23, 2018

Angular Universal on Google App Engine

東京Node学園祭2018での発表資料です。

267b5cb132e543e4bbac780632fd4742?s=128

Kenji Imamula

November 23, 2018
Tweet

Transcript

  1. Angular Universal on Google App Engine   ౦ژNodeֶԂࡇ2018 2018/11/23

    גࣜձࣾΧϒΫ ࠓଜݠ࢜ @kimamula ˞("&੒෼߇͑Ί
  2. About me •ࠓଜݠ࢜ •@kimamula •גࣜձࣾΧϒΫ •લ৬React->ݱ৬Angular •TypeScript ❤  

  3. Topics 1. What is Angular Universal 2. How to Angular

    Universal 3. Hack Angular Universal  
  4. 1. What is Angular Universal  

  5. Angular Universal https://github.com/angular/universal AngularΞϓϦέʔγϣϯͷSSRʢserver-side renderingʣΛՄೳʹ͢ΔͨΊͷϓϩδΣΫτ Server • Node.js • ASP.NET

    • (Python, Go, PHP) Page Request Browser "OHVMBS 6OJWFSTBM Rendered HTML • SEO • Performance  
  6. Our use case SSRαʔόʔ ʢGAE SE/Node.jsʣ APIαʔόʔ ʢGAE SE/Pythonʣ Ӷҙ։ൃத

     
  7. Google App EngineͱNode.js • 2018/06ɺSE (Standard Environment)Ͱ Node.jsΞϓϦͷσϓϩΠ͕Մೳʹ • ͨͩ͠ϕʔλ

    • Node.js 8, 10ʹରԠ • majorόʔδϣϯΛࢦఆͯ͠σϓϩΠ • minor, patchόʔδϣϯ͸উखʹ্͕Δ࢓༷  
  8. 2. How to Angular Universal  

  9. 2-1. Getting Started  

  10. Quick Start Prerequisite: Angular CLI ؆୯ʂʂ $ ng new my-project

    $ cd my-project $ ng add @nguniversal/express-engine --clientProject my-project $ npm run build:ssr && npm run serve:ssr # … Node Express server listening on http://localhost:4000  
  11. Slow Start https://angular.io/guide/universal • ެࣜυΩϡϝϯτ • આ໌ΛಡΈͳ͕ΒखॱΛͳͧΔ͜ͱͰɺ͋ Δఔ౓࢓૊ΈΛཧղͰ͖Δ • ऴ൫ͰQuick

    Startʹॻ͍ͨखॱ͕ग़͖ͯͯ3 ෼ΫοΩϯάײ͕͋Δ  
  12. Slow Start https://github.com/angular/universal-starter • ࠷খߏ੒ͷAngular UniversalΞϓϦ • angular.ioͷهࡌ಺༰ΑΓ΋ҰาઌΛߦͬͯ ͍ΔײʢϏϧυͷ࢓ํ͕ҧͬͨΓ͢Δʣ •

    Prerenderingʢޙड़ʣͷ࣮૷͋Γ  
  13. 2-2. Angular Universal ։ൃͷجຊ  

  14. αʔόʔͱΫϥΠΞϯτͰίʔ υ͕ڞ௨ʹͳΔ͜ͱʹ஫ҙ͢Δ • αʔόʔ/ΫϥΠΞϯτͷͲͪΒ͔Ͱ͔͠ಈ͔ ͨ͘͠ͳ͍ॲཧΛద੾ʹ੍ޚ͢Δ • αʔόʔͰ࣮ߦࡁΈͷॲཧ͕ΫϥΠΞϯτͰ ॏෳ࣮ͯ͠ߦ͞Εͳ͍Α͏ʹ͢Δ  

  15. αʔόʔͱΫϥΠΞϯτͰίʔ υ͕ڞ௨ʹͳΔ͜ͱʹ஫ҙ͢Δ • αʔόʔ/ΫϥΠΞϯτͷͲͪΒ͔Ͱ͔͠ಈ͔ ͨ͘͠ͳ͍ॲཧΛద੾ʹ੍ޚ͢Δ • αʔόʔͰ࣮ߦࡁΈͷॲཧ͕ΫϥΠΞϯτͰ ॏෳ࣮ͯ͠ߦ͞Εͳ͍Α͏ʹ͢Δ  

  16. isPlatformBrowser, isPlatformServer • ॳظԽॲཧ͸྆؀ڥͰجຊతʹશ෦ಈ͘ • constructor, ngOnInit, … • ReactͷcomponentDidMountͷΑ͏ͳ΋ͷ

    ͸ͳ͍ • αʔόʔͷΈorΫϥΠΞϯτͷΈͰಈ͔ͨ͠ ͍ॲཧ͕͋Δ৔߹͸దٓ൑ఆ͕ඞཁ  
  17. isPlatformBrowser, isPlatformServer import { PLATFORM_ID } from '@angular/core'; import {

    isPlatformBrowser, isPlatformServer } from '@angular/common'; constructor( @Inject(PLATFORM_ID) private platformId: Object ) {} ngOnInit() { if (isPlatformBrowser(this.platformId)) { // Client only code. } if (isPlatformServer(this.platformId)) { // Server only code. } }  
  18. αʔόʔͱΫϥΠΞϯτͰίʔ υ͕ڞ௨ʹͳΔ͜ͱʹ஫ҙ͢Δ • αʔόʔ/ΫϥΠΞϯτͷͲͪΒ͔Ͱ͔͠ಈ͔ ͨ͘͠ͳ͍ॲཧΛద੾ʹ੍ޚ͢Δ • αʔόʔͰ࣮ߦࡁΈͷॲཧ͕ΫϥΠΞϯτͰ ॏෳ࣮ͯ͠ߦ͞Εͳ͍Α͏ʹ͢Δ  

  19. Transferstate αʔόʔαΠυͰॳظԽͨ͠ঢ়ଶΛΫϥΠΞϯ ταΠυʹ౉͢࢓૊Έ • HTML͚ͩͰ͸ΫϥΠΞϯτͰ࠶औಘ͕ඞཁ Server Page Request Browser ঢ়ଶͷॳظԽ

    "1*ίʔϧ ֤छܭࢉॲཧ Rendered HTML including State ແବͳ"1*ίʔϧɺ ࠶ܭࢉΛ๷͙  
  20. Transferstate: αʔόʔ import { TransferState } from '@angular/platform-browser'; import {

    TRANSFER_STATE_KEY } from './transfer-state-key'; constructor(transferState: TransferState) { // … transferState.onSerialize(TRANSFER_STATE_KEY, () => { return state; }); } import { makeStateKey } from '@angular/platform-browser'; export const TRANSFER_STATE_KEY = makeStateKey('MY_KEY'); ঢ়ଶڞ༗ͷͨΊ ͷΩʔͷ࡞੒ ԿΒ͔ͷํ๏ͰΞϓϦέʔγϣϯͷ ঢ়ଶΛऔಘͯ͠ฦ͢ʢFH/H3Yʣ  
  21. Transferstate: ΫϥΠΞϯτ import { TransferState } from '@angular/platform-browser'; import {

    TRANSFER_STATE_KEY } from './transfer-state-key'; constructor(transferState: TransferState) { if (transferState.hasKey(TRANSFER_STATE_KEY)) { const state = transferState.get( TRANSFER_STATE_KEY, defaultValue ); this.transferState.remove(TRANSFER_STATE_KEY); } } ౉͞Εͨ ঢ়ଶͷऔಘ  
  22. 2-3. ϋϚͬͨͱ͜Ζɺ ࠔͬͨͱ͜Ζ  

  23. styles.cssͷSSR https://github.com/angular/universal/issues/974  

  24. styles.cssͷSSR: ໰୊ • ίϯϙʔωϯτ͝ͱͷCSS • SSR͞ΕΔʢHTMLʹΠϯϥΠϯల։͞Ε Δʣ -> ॳظදࣔͷ࣌఺Ͱਖ਼͘͠ద༻͞ΕΔ •

    styles.cssʢάϩʔόϧͳCSSʣ • SSR͞ΕͣɺJSͷॲཧͷதͰΠϯϥΠϯల։ ͞ΕΔ -> ద༻·Ͱʹϥά͕Ͱ͖Δ  
  25. styles.cssͷSSR: workaround • ԼͷΑ͏ͳSCSSͷΈͷίϯϙʔωϯτΛ࡞Γ • AppServerModuleͷbootstrapͰࢦఆ͢Δ ৄࡉ: https://github.com/Angular-RU/angular-universal-starter/commit/dbb413b // inline-style.component.scss

    @import 'path/to/styles.scss'; @NgModule({ // ... bootstrap: [AppComponent, InlineStyleComponent] }) export class AppServerModule { // ... }  
  26. watchϞʔυͰ։ൃ͍ͨ͠ https://github.com/angular/universal-starter/issues/490  

  27. DX with a CSR Angular app ng serveͰ؆୯ʹwatchϞʔυʹͳΔ 1.ίʔυฤू 2.ࣗಈϦϏϧυ

    3.ϥΠϒϦϩʔυ  
  28. DX with a SSR Angular app ެࣜͷखॱʹهࡌͷscript͚ͩΛ࢖͏৔߹ 1.ίʔυฤू 2.खಈͰϏϧυίϚϯυ࣮ߦ •

    1͔ΒϏϧυͳͷͰΠϯΫϦϝϯλϧ͡Όͳ͍ 3.खಈͰNode.jsαʔόʔ࠶ىಈ 4.खಈͰϒϥ΢βϦϩʔυ  
  29. ͭΒ͍  

  30. ࣗલͰ΍ͬͯΈΔ ”ng build --watchͨ͠΋ͷΛ͞Βʹhookͯ͠ExpressͳͲʹܨ͛ΔͱΑͦ͞͏”  

  31. Ϗϧυ͢Δ΋ͷ͸3ͭ 1. ΫϥΠΞϯτ޲͚AngularΞϓϦ 2. αʔόʔ޲͚AngularΞϓϦ 3. Express࣮ߦίʔυ • 2ͷϏϧυ࢈෺ʹґଘ ->

    1, 2͸Angular CLIͷ࢓૊ΈͰɺ 3͸webpackͷwatchϞʔυͰwatch͢Δ  
  32. ϏϧυͷྲྀΕ ίʔυฤू -> ΫϥΠΞϯτ޲͚AngularΞϓϦͷϏϧυ -> αʔόʔ޲͚AngularΞϓϦͷϏϧυ -> Express࣮ߦ༻ίʔυͷϏϧυ -> αʔόʔ࠶ىಈʢnodemonʣ

    -> ϒϥ΢βϦϩʔυʢbrowser-syncʣ  
  33. ࠓͷͱ͜Ζ͋·ΓշదͰ͸ͳ͍ ίʔυฤू -> ΫϥΠΞϯτ޲͚AngularΞϓϦͷϏϧυ -> αʔόʔ޲͚AngularΞϓϦͷϏϧυ -> Express࣮ߦ༻ίʔυͷϏϧυ -> αʔόʔ࠶ىಈʢnodemonʣ

    -> ϒϥ΢βϦϩʔυʢbrowser-syncʣ ͓͓ΉͶ~30ඵ HMRʹ͢΂͖ʁ  
  34. 3. Hack Angular Universal  

  35. 3-1. Prerender  

  36. Prerender or SSR https://github.com/angular/universal-starter#build-time-prerendering-vs-server-side-renderingssr   Ϗϧυ࣌ʹੜ੒ͨ͠)5.-Λ ࣮ߦ࣌ʹ੩తʹ഑৴͢Δ

  37. How to prerender universal-starterϦϙδτϦͷprerender.tsΑΓൈਮʢҰ෦վมʣ ['/', '/lazy', '/lazy/nested'].forEach(route => { const

    fullPath = join(BROWSER_FOLDER, route); previousRender = previousRender .then(_ => renderModuleFactory(AppServerModuleNgFactory, { document: index, url: route, extraProviders: [ provideModuleMap(LAZY_MODULE_MAP) ] }) ).then(html => writeFileSync(join(fullPath, 'index.html'), html) ); });  
  38. How to prerender universal-starterϦϙδτϦͷprerender.tsΑΓൈਮʢҰ෦վมʣ   ['/', '/lazy', '/lazy/nested'].forEach(route =>

    { const fullPath = join(BROWSER_FOLDER, route); previousRender = previousRender .then(_ => renderModuleFactory(AppServerModuleNgFactory, { document: index, url: route, extraProviders: [ provideModuleMap(LAZY_MODULE_MAP) ] }) ).then(html => writeFileSync(join(fullPath, 'index.html'), html) ); }); QSFSFOEFS ͢ΔύεͷҰཡ ύεʹର͢Δ )5.-Λੜ੒ ੜ੒͞Εͨ)5.-Λ ϑΝΠϧʹग़ྗ
  39. CSR vs SSR vs Prerender $43 443 1SFSFOEFS ΫϥΠΞϯτ ෛՙ

    º ̋ ̋ αʔόʔ ෛՙ ̋ º ̋ ॊೈੑ ̋ ̋ º  
  40. CSR vs SSR vs Prerender $43 443 1SFSFOEFS ΫϥΠΞϯτ ෛՙ

    º ̋ ̋ αʔόʔ ෛՙ ̋ º ̋ ॊೈੑ ̋ ̋ º ͜͜Λ̋ʹ Ͱ͖ͳ͍͔  
  41. ಈతίϯςϯπͷPrerender Server • ೝূ • ೝՄ • ݴޠ ౳ͷ൑ఆ /some/path

    PrerenderࡁΈHTML ঢ়گʹԠͯ͡ద੾ͳ HTMLΛฦ͢ ͜͏͍͏͜ͱ͕Ͱ͖ͨΒૉఢ͔΋…ʁ ʢݕ౼தʣ  
  42. 3-2. HTTP/2 Server Push  

  43. HTTP/2 Server Push 4FSWFS #SPXTFS GET /css1.css Without Push 4FSWFS

    #SPXTFS GET /index.html Index.html With Push /css1.css (push) GET /index.html Index.html /css1.css  
  44. Server Push with Angular CLI ະαϙʔτ https://github.com/angular/angular-cli/issues/11946  

  45. Server Push with Other FWs ஌͍ͬͯΔݶΓͰ͸ • Polymer • Preact

    • Nuxt.js  
  46. ࡞ͬͯΈΔ 2௨Γͷ࡞Γํ • Preload link headersΛ࢖͏ํ๏ -> ࠾༻ • ؆୯ʢৄࡉ͸࣍ͷεϥΠυͰʣ

    • Node.jsͷAPI Λ࢖͏ํ๏ • v10Ͱexperimentalϑϥάͳ͠Ͱ࢖͑Δ Α͏ʹͳͬͨ • ExpressͰͷ࢖͍উख͕ࠓͻͱͭ  
  47. Preload link headers 3FWFSTF QSPYZ #SPXTFS GET /index.html Index.html /css1.css

    (push) /PEFKT GET /index.html Index.html Link: </css1.css>; rel=preload;  
  48. ͦͷύεͰඞཁͳϦιʔε͸ʁ ҎԼͷํ๏Ͱ൑ఆ • Ϗϧυ࣌ͷwebpackͷstats͔ΒɺϞδϡʔ ϧͱchunkͷඥ෇͚৘ใΛಘΔ • SSRͷࡍ͜ͷ৘ใΛݩʹɺϩʔυ͞ΕͨϞ δϡʔϧ͕ଐ͢ΔchunkΛpreload link headersʹ௥Ճ͢Δ

     
  49. ngx-server-push https://github.com/kimamula/ngx-server-push/ • ੒ޭ • angular.ioͷखॱͰ࡞͍ͬͯΕ͹ɺ਺ߦखΛՃ ͑Ε͹ಋೖͰ͖ΔΑ͏ʹ࡞ͬͨ • ͨͩ͠ɺࡉ͔͍ͱ͜Ζ͕৭ʑέΞͰ͖͍ͯͳ ͍ͷͰɺ࣮ઓ౤ೖͰ͖ΔϨϕϧͰ͸ͳ͍

     
  50. ·ͱΊ • Angular UniversalΛ࢝ΊΔʹ͋ͨͬͯಡΉ • https://angular.io/guide/universal • https://github.com/angular/universal- starter •

    αʔόʔͱΫϥΠΞϯτͰίʔυ͕ڞ௨ʹͳ Δ͜ͱʹ஫ҙ͢Δ  
  51. ·ͱΊ • αΫαΫwatchϞʔυͰ͖Δਓ͕͍ͨΒڭ͑ͯ ͍ͩ͘͞ • Prerender΋ݕ౼ͯ͠Έ·͠ΐ͏  

  52. Thank you!