Segredos não ditos de PWA - muito além do Web App Manifest

Segredos não ditos de PWA - muito além do Web App Manifest

Nessa palestra vamos descobrir quais coisas além dos conceitos básicos de Progressive Web Apps são ignoradas na hora de produzir uma aplicação web, de alta performance.

Bfcf5c88d40733a45ce754e6ce225a8b?s=128

Eduardo Matos

October 21, 2017
Tweet

Transcript

  1. SEGREDOS NÃO DITOS DE PWA M U I TO A

    L É M D O W E B A P P M A N I F E S T
  2. OI, EU SOU O E D U @eduardojmatos

  3. a maior plataforma de contratação de serviços do Brasil

  4. D E V N A E S T R A

    DA . C O M . B R
  5. H T T P : / / D I V

    E R S I DA D E . T E C H
  6. H T T P S : / / G I

    T H U B . C O M / F R O N T E N D B R
  7. OK, PWA O QUÊ?

  8. None
  9. Public Work Administrations era uma agência do governo dos EUA

    pra cuidar de grandes construções públicas na década de 30/40. Só de curiosidade mesmo.
  10. Progressive Web Apps é um 
 conjunto de conceitos:
 Rapidez,

    Integridade, Confiabilidade e Engajamento.
  11. Como uma “agência”, PWA contém algumas “auditorias” pra certificar que

    estamos seguindo esses conceitos.
  12. PWA É SÓ FRONT-END?

  13. NÃO

  14. BACK-END E DEVOPS TAMBÉM ESTÃO NO JOGO MUITAS COISAS ANTES

    DO FRONT-END PRECISAM DE CUIDADO
  15. SERVER SIDE PWA ❤ BACK-END

  16. GZIP SE VOCÊ AINDA NÃO ESTÁ USANDO, BEM… Nginx, Node,

    Apache, IIS.
 Todos têm possibilidade de enviar conteúdo 
 comprimido com GZIP
  17. GZIP NGINX # Enable gzip compression.
 gzip on; # Compression

    level (1-9) # 5 is a perfect compromise between size and CPU usage
 gzip_comp_level 5; # Don't compress anything that's already small and unlikely to shrink much
 gzip_min_length 256;
  18. npm install compression --save const compression = require('compression'); const express

    = require('express'); const app = express(); app.use(compression()); GZIP NODE
  19. IMAGE COMPRESSION IMAGENS NA WEB NÃO PRECISAM TER 100% DE

    QUALIDADE
  20. 34KB 11KB

  21. IMAGE COMPRESSION FERRAMENTAS PARA OTIMIZAÇÃO jpegoptim SVGO pngquant 2 Gifsicle

  22. AUTOMATIZE imagemin-webpack-plugin gulp-imagemin middleman-imageoptim imagemin USE FERRAMENTAS NO BUILD DO

    PROJETO PRA COMPRESSÃO TODAS USAM QUASE AS MESMAS LIBS DE OTIMIZAÇÃO
  23. TIME TO FIRST BYTE O TEMPO PRA QUE O PRIMEIRO

    BYTE CHEGUE NO CLIENT
  24. TIME TO FIRST BYTE • Tempo pra envio da requisição

    HTTP; • Tempo para o servidor processar a requisição; • Tempo para o servidor responder o primeiro byte;
  25. TIME TO FIRST BYTE http://bytecheck.com/

  26. TIME TO FIRST BYTE TEMPO DE SERVIDOR, REQUEST TIME… •

    Aqui não tem jeito: precisa melhorar o tempo de resposta do servidor; • Dá pra usar cache na frente pra amenizar (Varnish, ou outra tecnologia); • Ou uma CDN (veremos logo mais).
  27. TIME TO FIRST BYTE É IMPORTANTE PARA A PRIMEIRA EXPERIÊNCIA

    DO PWA. SENSAÇÃO DE VELOCIDADE É UM DOS ASPECTOS MAIS IMPORTANTES NUM WEB APP.
  28. SINCRONIA DE DADOS SUPORTE OFFLINE NO BROWSER, E SINCRONIA COM

    BACK-END
  29. SINCRONIA DE DADOS POUCHDB É UMA IMPLEMENTAÇÃO COM JAVASCRIPT DO

    COUCHDB. O OBJETIVO É EMULAR O COUCHDB RODANDO NO BROWSER OU ATÉ MESMO NO NODEJS
  30. const PouchDB = require('pouchdb'); // sample === database name const

    db = new PouchDB('http://localhost:5984/sample'); const myUser = { _id: 'Edu-v1', name: 'Eduardo Matos', email: 'eduardoj.matos@gmail.com', company_name: 'GetNinjas', };
  31. db.find({ selector: { _id: myUser._id } }) .then((result) => {

    db.put(myUser) .then(doc => console.log('created:', doc)) .catch((err) => { const updatedUser = Object.assign({ _rev: result.docs[0]._rev }, myUser); if (err.name === 'conflict') { db.put(updatedUser).then((user) => { console.log('putted =>', user); }); } }); });
  32. SINCRONIA DE DADOS const PouchDB = require('pouchdb'); // sample ===

    database name const db = new PouchDB('sample'); db.sync('http://localhost:5984/sample', { live: true, retry: true }); VERSÃO “NO-BRAINER”
  33. SINCRONIA DE DADOS POUCHDB É BUILT-IN EM DIVERSOS ENVIRONMENTS

  34. DEVOPS PWA ❤ DEVOPS

  35. PAGESPEED MÓDULO DE OTIMIZAÇÃO PARA NGINX E APACHE

  36. PAGESPEED MAIS DE 40 FILTROS DE OTIMIZAÇÃO Combine CSS Combine

    JS Filters and Options for Optimizing Images Prioritize Critical CSS Lazily Load Images Remove Quotes Move CSS to Head Inline Preview Images Inline Google Fonts API CSS Hint Resource Preloading
  37. PAGESPEED MAIS DE 40 FILTROS DE OTIMIZAÇÃO pagespeed on; pagespeed

    FileCachePath /var/ngx_pagespeed_cache; pagespeed LogDir /var/log/pagespeed; # config pagespeed LowercaseHtmlNames on; pagespeed EnableFilters remove_comments; pagespeed EnableFilters collapse_whitespace; pagespeed EnableFilters remove_quotes; pagespeed EnableFilters elide_attributes;
  38. SEUS AMIGUINHOS DA PERFORMANCE DNS-PREFETCH, PRECONNECT, E PRELOAD

  39. DNS-PREFETCH OLHA SÓ, VAI VIR ALGO DESSE DOMÍNIO DNS-Prefetch Notifica

    o cliente que há assets que vão ser baixados dentro de um domínio, assim que o browser resolver o DNS do mesmo.
  40. DNS-PREFETCH OLHA SÓ, VAI VIR ALGO DESSE DOMÍNIO <link rel="dns-prefetch"

    href="//abs-0.twimg.com">
  41. DNS-PREFETCH OLHA SÓ, VAI VIR ALGO DESSE DOMÍNIO dns lookup

    | connect | ssl <link rel="dns-prefetch" href="//abs-0.twimg.com">
  42. DNS-PREFETCH OLHA SÓ, VAI VIR ALGO DESSE DOMÍNIO connect |

    ssl <link rel="dns-prefetch" href="//abs-0.twimg.com">
  43. PRECONNECT EI! JÁ CONECTA AÍ! Preconnect Parecido com o dns-prefetch,

    o preconnect faz o hand-shake e negociação de TLS de forma opcional.
  44. PRECONNECT EI! JÁ CONECTA AÍ! sem dns lookup, connect e

    ssl! <link rel="preconnect" href="//abs-0.twimg.com">
  45. PRECONNECT BÔNUS! <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin> PRA FAZER O PRECONNECT

    COM A API DO GOOGLE FONTS E EVITAR O HAND-SHAKE ;)
  46. Preload Faz o download de um asset, independente do browser

    achar que precisa ou não. PRELOAD JÁ BAIXA AÍ, POR FAVOR
  47. PRELOAD JÁ BAIXA AÍ, POR FAVOR <link rel="preload" href="style.css" as="style">

    <link rel="preload" href="main.js" as="script"> O “as” suporta:
 audio, document, embed, fetch, font, image, object, script, style, track, worker, video
  48. PRELOAD JÁ BAIXA AÍ, POR FAVOR em 0.6 segundos, já

    iniciou o download, e em 149ms respondeu <link rel="preload" as="script" 
 crossorigin="anonymous" 
 href="https://abs-0.twimg.com/runtime.bfc3890d94f2bd96.js" />
  49. CDN DISTRIBUINDO SEUS ASSETS RAPIDAMENTE, COM CACHE E POR REGIÕES

  50. CDN DISTRIBUINDO SEUS ASSETS RAPIDAMENTE, COM CACHE E POR REGIÕES

  51. HTTP/2 A CAMADA DE TRANSPORTE MODERNA E ÁGIL

  52. HTTP/2 REQUESTS PARALELOS E NÃO BLOCANTES

  53. HTTP/2 HEADER COMPRESSION

  54. HTTP/2 HPACK Quase 88% de redução no tamanho do request

  55. FRONT-END FRONT PIRA NO PWA

  56. IMAGENS NO TAMANHO CERTO 40X40? NÃO USE UMA DE 50X50

  57. resolução tamanho natural display size (CSS px) Pixels desnecessários bytes

    
 desnecessários 1x 110 x 110 100 x 100 110 x 110 - 100 x 100 = 2100 2100 x 4 / 1024 = 
 8 KB 1x 410 x 410 400 x 400 410 x 410 - 400 x 400 = 8100 8100 x 4 / 1024 = 
 31.6 KB 1x 810 x 810 800 x 800 810 x 810 - 800 x 800 = 16100 16100 x 4 / 1024 =
 62.9 KB 2x 220 x 220 100 x 100 210 x 210 - (2 x 100) x (2 x 100) = 8400 8400 x 4 / 1024 =
 32.8 KB 2x 820 x 820 400 x 400 820 x 820 - (2 x 400) x (2 x 400) = 32400 32400 x 4 / 1024 =
 126.5 KB 2x 1620 x 1620 800 x 800 1620 x 1620 - (2 x 800) x (2 x 800) = 64400 64400 x 4 / 1024 =
 251.6 KB
  58. SOBRE FONTES… 
 USE MENOS! • MAIS QUE 2 FAMÍLIAS

    DE FONTES NUM WEB APP… PARECE ESTRANHO! • FONTES SÃO BLOCANTES; • CAUSAM FOIT (FLASH OF INVISIBLE TEXT);
  59. PRA EVITAR FOIT FONT FACE OBSERVER

  60. <html class="default-font"> <style> .default-font { font-family: Arial, sans-serif; } </style>

  61. var fonts = [ { name: 'Lato', weight: 400 },

    { name: 'Lato', weight: 700 }, { name: 'Lato', weight: 900 } ]; fonts.forEach(function(font, index) { fonts[index].observer = new FontFaceObserver(font.name, { 
 weight: font.weight 
 }); }); Promise .all(fonts.map(function(font) { return font.observer.load(); })) .then(() => document.body.classList.remove("default-font") );
  62. None
  63. None
  64. None
  65. INPUT LATENCY SENSAÇÃO DE VELOCIDADE NO INPUT DO USUÁRIO •

    O ideal é a interação de input ser menor que 100ms. • Digitar, Clicar, Mover sobre hover, etc. Mais que 100ms, repense muito!
  66. UM SIMPLES EFEITO DE HOVER COM 2S…

  67. GPU mouseover com transition e animation Paint, Recalculate Style

  68. document.addEventListener( 'touchstart', onTouchStartHandler, { passive: true }); TOUCH MOVE, START,

    ETC., SEM O DOUBLE CHECK DE “TOUCH OR SCROLL" TOUCH PASSIVE
  69. https://www.youtube.com/watch?v=65VMej8n23A TOUCH PASSIVE

  70. PRIORIZANDO O ESSENCIAL PRO USUÁRIO CRITICAL PATH above the fold

  71. CSS INLINE CRITICAL PATH

  72. CRITICAL PATH ATENÇÃO! MUITO INLINE PREJUDICA O TTFB!

  73. QUANDO O USUÁRIO VAI COMEÇAR A VER SUA PÁGINA DE

    VERDADE FIRST MEANINGFUL PAINT
  74. LEMBRA DO PREFETCH E PRELOAD? POIS É! FIRST MEANINGFUL PAINT

  75. NÃO É ESSENCIAL DE INÍCIO? CARREGA DEPOIS! JAVASCRIPT NÃO BLOCANTE

    <!doctype html> <html> <head> <title>Testing</title> </head> <body> <script src="main.js" async></script> <script src="page.js" async></script> </body> </html>
  76. NÃO É ESSENCIAL DE INÍCIO? CARREGA DEPOIS! JAVASCRIPT NÃO BLOCANTE

    Com async, o JavaScript é carregado enquanto o DOM é construído. Assim que carregado ele já é executado.
  77. NÃO É ESSENCIAL DE INÍCIO? CARREGA DEPOIS! JAVASCRIPT NÃO BLOCANTE

    Com defer, o JavaScript é carregado enquanto o DOM é construído também, mas só é carregado quando o DOM está 100% pronto.
  78. NÃO É ESSENCIAL DE INÍCIO? CARREGA DEPOIS! JAVASCRIPT NÃO BLOCANTE

  79. USE ESTRATÉGIAS DE CACHE COM SERVICE WORKERS Como o Service

    Workers funciona
  80. O QUE É SERVICE WORKERS? // index.html if ('serviceWorker' in

    navigator) { navigator.serviceWorker.register('/js/sw.js') .then(reg => { if(reg.active) { console.log('Service worker active'); } }).catch(error => { console.error('Registration failed with', error); }); };
  81. O QUE É SERVICE WORKERS? // js/sw.js self.addEventListener('install', event =>

    { event.waitUntil( caches.open('v1').then(cache => { return cache.addAll([ '/index.html', '/js/app.js', '/css/styles.css', '/images/avatar/profile/01.jpg' ]); }) ); });
  82. O QUE É SERVICE WORKERS? // js/sw.js
 self.addEventListener('fetch', (event) =>

    { event.respondWith( caches.open('v1').then((cache) => { return cache.match(event.request).then((response) => { if (response) { return response.clone(); } throw Error('Response cached missing'); }).catch((error) => { return fetch(event.request.url); //fallback para online }); }) ); });
  83. None
  84. NETWORK FIRST? CACHE FIRST? SW-PRECACHE PRA CRIAR O ARQUIVO DO

    SW USE ESTRATÉGIAS DE CACHE COM SERVICE WORKERS
  85. NETWORK FIRST? CACHE FIRST? SW-PRECACHE PRA CRIAR O ARQUIVO DO

    SW USE ESTRATÉGIAS DE CACHE COM SERVICE WORKERS npm i sw-precache -g sw-precache —config=precache-config.js --verbose module.exports = { staticFileGlobs: [ 'app/css/**.css', 'app/**.html', 'app/images/**.*', 'app/js/**.js' ], stripPrefix: 'app/', runtimeCaching: [{ handler: 'cacheFirst' }] };
  86. PASSE UMA “AUDITORIA” NO SEU WEB APP https://github.com/GoogleChrome/lighthouse/

  87. TAVA BOM, NÃO TAVA MUITO BOM, TAVA MEIO RUIM TAMBÉM,

    AGORA PARECE QUE PIOROU
  88. WEB APPS CONTINUAM SENDO COMPLEXAS…

  89. PWA SÓ ADICIONOU “AUDITORIAS” PRA AVALIAR SEU WEB APP CTZ

    QUE VÃO ME XINGAR
  90. A MAIORIA DOS PADRÕES DO PWA JÁ EXISTIAM

  91. RESUMINDO…

  92. HÁ MUITO POR TRÁS DE APENAS UM WEB APP MANIFESTO

    COM ÍCONE DE APP E INSTALAÇÃO NO DEVICE
  93. BACK-END DEVOPS FRONT-END PWA NÃO É SÓ A RESPONSA DE

    UMA ÁREA EM TECNOLOGIA
  94. DÁ PRA USAR ESSAS DICAS MESMO NÃO CONSTRUINDO UM PWA

  95. E NÃO, OS APPS NATIVOS NÃO VÃO MORRER TÃO CEDO

    ;) CTZ QUE VÃO ME XINGAR [2]
  96. @eduardojmatos http://eduardomatos.me eduardoj.matos@gmail.com OBRIGADO ;)