Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting Into HNPWA with Vue.js at Google I/O 20...

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

Jimmy Moon

June 11, 2017
Tweet

More Decks by Jimmy Moon

Other Decks in Programming

Transcript

  1. Hacker News ? is a social news website focusing on

    computer science and entrepreneurship. running by y combinator, Paul Graham, that have many of clones
  2. 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)
  3. 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)
  4. 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
  5. 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 } )
  6. 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 ' } ] } )
  7. 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 ( ) } )
  8. 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 > } )
  9. 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 > . . . ` ) } ) } )
  10. 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 >
  11. 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 ) } )
  12. 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
  13. 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 } }
  14. 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 ' )
  15. 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 }
  16. 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 ) } ) } )
  17. 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 ' } . . . } )
  18. 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 ' } . . . } )
  19. 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 : [ / / . . . ] } ) }
  20. 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 } }
  21. 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 ) } ) }
  22. 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 ) } } ) } )
  23. 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 } } } ) }
  24. 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 } }
  25. 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 ) } ) } . . . } }
  26. 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 ( ) } ) } )
  27. 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 ( ) } } } )
  28. 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 _ _ ) }
  29. 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 '
  30. 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 ' ) ;
  31. 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 ' } }
  32. AND MORE Lazy / Async Components Extracting Common Chunks Ultra

    Lightweight Frameworks Using the Platform
  33. 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