Getting Into HNPWA with Vue.js at Google I/O 2017 Extended in ICN, Korea

Getting Into HNPWA with Vue.js at Google I/O 2017 Extended in ICN, Korea

Presentation at Google I/O 2017 Extended in ICN, Korea

687ac25540fe35fcb5e828f75c4a6079?s=128

Jimmy Moon

June 11, 2017
Tweet

Transcript

  1. GETTING INTO

  2. None
  3. None
  4. None
  5. None
  6. None
  7. Hacker News ? is a social news website focusing on

    computer science and entrepreneurship. running by y combinator, Paul Graham, that have many of clones
  8. None
  9. None
  10. None
  11. CHECKPOINT Progressive Web App All of views (Top, New, Best

    ...) Display 30 items per-page for story list views 90/100 score from Lighthouse First Interactive in under 5s via webpagetest(w/ TTI score of LH)
  12. AND MORE ... Manual inspection of app's timeline (by addy

    osmani) Application Shell pattern (mandatory) Best to work cross-browser (optional) Supports offline caching of HN data (optional) Server-side rendering (optional)
  13. JAVASCRIPT FRAMEWORKS

  14. None
  15. None
  16. React Vue.js Preact Polymer CLI for PWA yarn react‑app vue

    init pwa preact create polymer init Service Worker Code‑Splitting HTML Import Preloaded / Prefeched SSR
  17. Vue.js

  18. PREPARATION > npm install -g vue-cli

  19. SCAFFOLDING > vue init pwa vue-hn-pwa && cd $_ &&

    yarn install
  20. FIRST LOOK > yarn build && yarn start

  21. STORY VIEW > yarn add firebase-hackernews

  22. None
  23. Fetch top story h a c k e r n

    e w s ( ) . s t o r i e s ( ' t o p ' ) . t h e n ( i t e m s = > { t h i s . i t e m s = i t e m s } )
  24. MORE VIEWS Toolbar, Comment and User profile

  25. SERVICE WORKER

  26. SWPrecacheWebpackPlugin n e w S W P r e c

    a c h e W e b p a c k P l u g i n ( { c a c h e I d : ' m y - v u e - a p p ' , s t a t i c F i l e G l o b s : [ ' d i s t / * * / * . { j s , h t m l , c s s , p n g , s v g , j s o n } ' ] , . . . r u n t i m e C a c h i n g : [ { u r l P a t t e r n : ' / ' , h a n d l e r : ' n e t w o r k F i r s t ' } , { u r l P a t t e r n : / \ / ( t o p | n e w | s h o w | a s k | j o b s ) / , h a n d l e r : ' n e t w o r k F i r s t ' } ] } )
  27. None
  28. Workbox example i m p o r t S c

    r i p t s ( ' h t t p s : / / u n p k g . c o m / w o r k b o x - s w @ 0 . 0 . 2 ' ) c o n s t w o r k b o x S W = n e w W o r k b o x S W ( { c l i e n t s C l a i m : t r u e } ) w o r k b o x S W . p r e c a c h e ( [ ] ) w o r k b o x S W . r o u t e r . s e t D e f a u l t H a n d l e r ( { h a n d l e r : w o r k b o x S W . s t r a t e g i e s . s t a l e W h i l e R e v a l i d a t e ( ) } )
  29. SERVER SIDE RENDERING

  30. None
  31. SERVER SIDE RENDERING > yarn add vue-server-renderer

  32. rendering sample standalone c o n s t r e

    n d e r e r = r e q u i r e ( ' v u e - s e r v e r - r e n d e r e r ' ) . c r e a t e R e n d e r e r ( ) r e n d e r e r . r e n d e r T o S t r i n g ( a p p , ( e r r , h t m l ) = > { i f ( e r r ) t h r o w e r r c o n s o l e . l o g ( h t m l ) / / = > < d i v d a t a - s e r v e r - r e n d e r e d = " t r u e " > / / = > h e l l o w o r l d / / = > < / d i v > } )
  33. SERVER SIDE RENDERING > npm install express --save

  34. rendering sample with express c o n s t s

    e r v e r = r e q u i r e ( ' e x p r e s s ' ) ( ) c o n s t r e n d e r e r = r e q u i r e ( ' v u e - s e r v e r - r e n d e r e r ' ) . c r e a t e R e n d e r e r ( ) s e r v e r . g e t ( ' * ' , ( r e q , r e s ) = > { r e n d e r e r . r e n d e r T o S t r i n g ( a p p , ( e r r , h t m l ) = > { . . . r e s . e n d ( ` . . . < b o d y > $ { h t m l } < / b o d y > . . . ` ) } ) } )
  35. SERVER SIDE RENDERING Page Template

  36. index.template.html < ! D O C T Y P E

    h t m l > < h t m l l a n g = " e n " > < h e a d > < t i t l e > H e l l o < / t i t l e > < / h e a d > < b o d y > < ! - - v u e - s s r - o u t l e t - - > < / b o d y > < / h t m l >
  37. renderer with template c o n s t r e

    n d e r e r = c r e a t e R e n d e r e r ( { t e m p l a t e : r e q u i r e ( ' f s ' ) . r e a d F i l e S y n c ( ' . / i n d e x . t e m p l a t e . h t m l ' , ' u t f - 8 ' ) } ) r e n d e r e r . r e n d e r T o S t r i n g ( a p p , ( e r r , h t m l ) = > { / / w i l l b e t h e f u l l p a g e w i t h a p p c o n t e n t i n j e c t e d c o n s o l e . l o g ( h t m l ) } )
  38. SERVER SIDE RENDERING Universal Code

  39. s r c ├ ─ ─ c o m p

    o n e n t s │ ├ ─ ─ F o o . v u e │ ├ ─ ─ B a r . v u e │ └ ─ ─ B a z . v u e ├ ─ ─ A p p . v u e ├ ─ ─ a p p . j s # u n i v e r s a l e n t r y ├ ─ ─ e n t r y - c l i e n t . j s # r u n s i n b r o w s e r o n l y └ ─ ─ e n t r y - s e r v e r . j s # r u n s o n s e r v e r o n l y
  40. app.js, universal entry i m p o r t V

    u e f r o m ' v u e ' i m p o r t A p p f r o m ' . / A p p . v u e ' e x p o r t f u n c t i o n c r e a t e A p p ( ) { c o n s t a p p = n e w V u e ( { r e n d e r : h = > h ( A p p ) } ) r e t u r n { a p p } }
  41. entry-client.js i m p o r t { c r

    e a t e A p p } f r o m ' . / a p p ' c o n s t { a p p } = c r e a t e A p p ( ) / / m o u n t i n g o f v u e i s n t a n c e a p p . $ m o u n t ( ' # a p p ' )
  42. entry-server.js: i m p o r t { c r

    e a t e A p p } f r o m ' . / a p p ' / / w i t h o u t m o u n t i n g o f v u e i s n t a n c e e x p o r t d e f a u l t c o n t e x t = > { c o n s t { a p p } = c r e a t e A p p ( ) r e t u r n a p p }
  43. server.js c o n s t c r e a

    t e A p p = r e q u i r e ( ' . / a p p ' ) s e r v e r . g e t ( ' * ' , ( r e q , r e s ) = > { c o n s t a p p = c r e a t e A p p ( ) r e n d e r e r . r e n d e r T o S t r i n g ( a p p , ( e r r , h t m l ) = > { / / h a n d l e e r r o r . . . r e s . e n d ( h t m l ) } ) } )
  44. webpack.client.conf.js v a r w e b p a c

    k C o n f i g = m e r g e ( b a s e W e b p a c k C o n f i g , { e n t r y : { a p p : ' . / s r c / e n t r y - c l i e n t . j s ' } . . . } )
  45. webpack.server.conf.js v a r w e b p a c

    k C o n f i g = m e r g e ( b a s e W e b p a c k C o n f i g , { e n t r y : { a p p : ' . / s r c / e n t r y - s e r v e r . j s ' } . . . } )
  46. SERVER SIDE RENDERING Routing on Server

  47. router.js, export generator i m p o r t R

    o u t e r f r o m ' v u e - r o u t e r ' V u e . u s e ( R o u t e r ) e x p o r t f u n c t i o n c r e a t e R o u t e r ( ) { r e t u r n n e w R o u t e r ( { m o d e : ' h i s t o r y ' , r o u t e s : [ / / . . . ] } ) }
  48. app.js, create routing e x p o r t f

    u n c t i o n c r e a t e A p p ( ) { c o n s t r o u t e r = c r e a t e R o u t e r ( ) c o n s t a p p = n e w V u e ( { r o u t e r , r e n d e r : h = > h ( A p p ) } ) r e t u r n { a p p , r o u t e r } }
  49. entry-server.js, matched components e x p o r t d

    e f a u l t c o n t e x t = > { r e t u r n n e w P r o m i s e ( ( r e s o l v e , r e j e c t ) = > { c o n s t { a p p , r o u t e r } = c r e a t e A p p ( ) r o u t e r . p u s h ( c o n t e x t . u r l ) / / f r o m s e r v e r . j s r o u t e r . o n R e a d y ( ( ) = > { i f ( r o u t e r . g e t M a t c h e d C o m p o n e n t s ( ) . l e n g t h > 0 ) { r e s o l v e ( a p p ) } } , r e j e c t ) } ) }
  50. server.js, context with pass url s e r v e

    r . g e t ( ' * ' , ( r e q , r e s ) = > { c o n s t c o n t e x t = { u r l : r e q . u r l } c r e a t e A p p ( c o n t e x t ) . t h e n ( a p p = > { r e n d e r e r . r e n d e r T o S t r i n g ( a p p , ( e r r , h t m l ) = > { . . . r e s . e n d ( h t m l ) } } ) } )
  51. SERVER SIDE RENDERING Data Pre-Fetching and Hydration

  52. vue-hn-mixin.js, sharing data interface f u n c t i

    o n i n s t a l l ( V u e ) { V u e . m i x i n ( { b e f o r e C r e a t e ( ) { c o n s t { p a r e n t , h n } = t h i s . $ o p t i o n s i f ( h n ) { t h i s . $ h n = h n } e l s e i f ( p a r e n t & & p a r e n t . $ h n ) { t h i s . $ h n = p a r e n t . $ h n } } } ) }
  53. app.js, create routing i m p o r t H

    a c k e r n e w s f r o m ' . / m i x i n s / v u e - h n - m i x i n ' V u e . u s e ( H a c k e r n e w s ) e x p o r t f u n c t i o n c r e a t e A p p ( ) { . . . c o n s t h n = h a c k e r n e w s ( ) c o n s t a p p = n e w V u e ( { . . . h n } ) r e t u r n { a p p , r o u t e r , h n } }
  54. router.js, set class method f u n c t i

    o n c r e a t e S t o r y ( t y p e ) { r e t u r n { . . . a s y n c D a t a ( { h n , r o u t e } ) { r e t u r n h n . s t o r i e s ( t y p e , { p a g e : N u m b e r ( r o u t e . p a r a m s . p a g e | | 1 ) } ) } . . . } }
  55. entry-server.js, call class method r e t u r n

    n e w P r o m i s e ( ( r e s o l v e , r e j e c t ) = > { c o n s t { a p p , r o u t e r , h n } = c r e a t e A p p ( ) r o u t e r . p u s h ( c o n t e x t . u r l ) / / f r o m s e r v e r . j s . . . r o u t e r . o n R e a d y ( ( ) = > { . . C o m p o n e n t . a s y n c D a t a ( { h n , r o u t e : r o u t e r . c u r r e n t R o u t e } ) } ) . t h e n ( ( ) = > { / / f l u s h d a t a f o r h y d r a t i o n o n c l i n e t c o n t e x t . s t a t e = h n . d a t a C a c h e d ( ) } ) } )
  56. entry-client.js, call calss methond V u e . m i

    x i n ( { b e f o r e R o u t e U p d a t e ( t o , f r o m , n e x t ) { c o n s t { a s y n c D a t a } = t h i s . $ o p t i o n s i f ( a s y n c D a t a ) { a s y n c D a t a ( { h n , r o u t e : t o } ) . t h e n ( n e x t ) . c a t c h ( n e x t ) } e l s e { n e x t ( ) } } } )
  57. entry-client.js, hydration c o n s t { a p

    p , r o u t e r , h n } = c r e a t e A p p ( ) / / h y d r a t i n g i n i t i a l s t a t e i n t o g l o b a l o b j e c t o n c l i e n t i f ( w i n d o w . _ _ I N I T I A L _ S T A T E _ _ ) { h n . d a t a C a c h e d ( w i n d o w . _ _ I N I T I A L _ S T A T E _ _ ) }
  58. None
  59. None
  60. None
  61. None
  62. OPTIMIZATION For First interactive time in 5s

  63. None
  64. None
  65. CODE-SPLITTING Reduce download time, Push critical path resource by preload

    / prefetch
  66. None
  67. None
  68. None
  69. None
  70. None
  71. LIVE-CODE IMPORTING aka, Dead code elimination

  72. webpack-bundle-analyzer

  73. None
  74. Include only the features you need - ` i m

    p o r t f i r e b a s e f r o m ' f i r e b a s e ' + i m p o r t f i r e b a s e f r o m ' f i r e b a s e / a p p ' + i m p o r t ' f i r e b a s e / d a t a b a s e '
  75. ragingwind/firebase-hackernews/es c o n s t f i r e

    b a s e = r e q u i r e ( ' f i r e b a s e / a p p ' ) c o n s t _ = r e q u i r e ( ' f i r e b a s e / d a t a b a s e ' ) ;
  76. webpack.client.conf.js r e s o l v e : {

    a l i a s : { ' f i r e b a s e - h a c k e r n e w s ' : ' f i r e b a s e - h a c k e r n e w s / e s ' } }
  77. None
  78. None
  79. None
  80. AND MORE Lazy / Async Components Extracting Common Chunks Ultra

    Lightweight Frameworks Using the Platform
  81. None
  82. AUDIT Lighthouse

  83. DEPLOYMENT Shipping your app on the live server

  84. Firebase / / i n s t a l l

    f i r e b a s e t o o l n p m i n s t a l l - g f i r e b a s e - t o o l s / / l o g i n i n t o f i r e b a s e v i a f i r e b a s e c l i t o o l f i r e b a s e l o g i n / / i n i t p r o j e c t f i r e b a s e i n i t / / d e p l o y f i r e b a s e d e p l o y - - o n l y h o s t i n g
  85. None
  86. Web Page Test aka, WBT

  87. None
  88. None
  89. None
  90. Thanks Go make it and submit