Du "make it work" au "make it fast"

Des quickwins pour booster votre application !

Jonathan Hervieux @djow__

#DevoxxFR

-75%

encore 14m39s !

  • Amabay
  • Cdiscount
  •  Rakuten

 = 

100ms en +

1% de CA en -

Jonathan Hervieux

Développeur

@Takima / @Cartier

1. Du vocabulaire

Indicateurs de performance, les web vitals

1. Du vocabulaire

- FCP (First Contentful Paint)

- TTFB (Time To First Byte)

1. Du vocabulaire

- TBT (Total Blocking Time)

- TTI (Time To Interactive)

1. Du vocabulaire

1. Du vocabulaire

1. Du vocabulaire

  • Les web vitals        
  • FCP, LCP, CLS...                   

 

📝 Take Away

✔️

✔️

 

Comment on les mesure ?  

 

2. Comment mesurer les web vitals ? 

LightHouse

Avec une simulation

- DevTools

2. Comment mesurer les web vitals ?

- DevTools

 

- Sur un outil de CI/CD avec ligthouse-ci

Avec une simulation

2. Comment mesurer les web vitals ?

- DevTools

 

- Sur un outil de CI/CD avec ligthouse-ci

Avec une simulation

En conditions réelles

- librairie js web-vitals

import { getCLS, getFID, getLCP } from 'web-vitals';

getCLS(console.log);
getFID(console.log);
getLCP(console.log);

2. Comment mesurer les web vitals ?

2. Comment mesurer les web vitals ?

Simuler environnement utilisateur 🐌

2. Comment mesurer les web vitals ?

2. Comment mesurer les web vitals ?

2. Comment mesurer les web vitals ?

1. Du vocabulaire

  • Les web vitals        
  • FCP, LCP, CLS...                         

 

📝 Take Away

✔️

✔️

 

Comment on les améliore ?  

 

2. Mesurer les web vitals

  • DevTools / lightouse-ci        
  • librairie web-vitals                   

 

✔️

✔️

T = 0

T = beaucoup de temps après T0

html
css
js
png / jpg

temps (ms)

FCP

réseau

CPU

TTI

}

exécution

3. Améliorer les web-vitals

3. Améliorer les web vitals

Envoyer moins de contenu

Images

3. Améliorer les web vitals

WebP

AVIF

JPEG XL

<picture>
  <source srcset="image.avif" type="image/avif" />
  <source srcset="image.jxl" type="image/jxl" />
  <source srcset="image.webp" type="image/webp" />
  <img src="image.jpg" alt="An amazing image" />
</picture>

527ko en JPG -> 222ko en WebP

-60%

3. Améliorer les web vitals

Images

Javascript

214 Mo
1.49 Mo

3. Améliorer les web vitals

webpack-bundle-analyzer

Javascript

3. Améliorer les web vitals

//fonction qui retourne le double d'un nombre
 function double(nombre) {
     return nombre * 2;
     
 }

Minification

function d(n){return n*2}

98 caractères VS 25 caractères

Javascript

3. Améliorer les web vitals

module.exports = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  }
}
webpack.config.js

1.49 Mo -> 617 ko

-59%

Javascript

3. Améliorer les web vitals

bundlephobia

Javascript

3. Améliorer les web vitals

1.49 Mo -> 376 ko

-75%

webpack-bundle-analyzer
import _ from 'lodash'

Javascript

3. Améliorer les web vitals

Tree shaking 🌳

Javascript

{
  "name": "Amabay",
  ...
  "sideEffects": [
    "**/*.css",
    "**/*.scss",
    "src/config/config-with-side-effects.js"
  ]
}
package.json

comportement global ~= polyfill, css, ...

3. Améliorer les web vitals

module.exports = {
  // ...
  optimization: {
    usedExports: true,
    sideEffects: true,
  }
}
webpack.config.js

376 ko -> 320 ko

-15%

3. Améliorer les web vitals

Javascript

Tree shaking

{
  // ...
  plugins: [
    // ...
    new PurgecssPlugin({
      paths: glob.sync(`${path.join(__dirname, './src')}/**/*`,  { nodir: true }),
    }),
    new OptimizeCssAssetsPlugin(),
  ]
}

9.88 ko -> 7.88 ko

-20%

Minification

PurgeCssPlugin

OptimizeCssAssetsPlugin

webpack.config.js

CSS

3. Améliorer les web vitals

Gzip 

brotli

webpack.config.js
{
  // ...
  plugins: [
    // ...
    new CompressionPlugin({
      filename: '[path].gz',
      algorithm: 'gzip',
      test: /\.(js|css|html)$/,
      minRatio: 0.8,
    }),
    new CompressionPlugin({
      filename: '[path].br',
      algorithm: 'brotliCompress',
      test: /\.(js|css|html)$/,
      compressionOptions: { level: 11 },
      minRatio: 0.8,
    }),
  ]
}

-55%

-60%

Compression

3. Améliorer les web vitals

1.94 Mo
599 ko

~70%

FCP

9.3s -> 3.3s

LCP

11.5s -> 6.4s

AVANT / APRES

3. Améliorer les web vitals

1. Du vocabulaire

  • Les web vitals        
  • FCP, LCP, CLS...                         

 

📝 Take Away

✔️

✔️

2. Mesurer les web vitals

  • DevTools / lightouse-ci        
  • librairie web-vitals                   

 

✔️

✔️

3. Améliorer les web vitals

  • Utiliser des formats modernes
  • Minification + tree shaking
  • S'applique aussi au CSS
  • Compresser      

 

✔️

✔️

✔️

✔️

T = 0

html
css
js
png / jpg

temps (ms)

FCP

réseau

CPU

TTI

}

exécution

T = un peu moins de temps après

3. Améliorer les web vitals

temps CPU

3. Améliorer les web vitals

Exécuter un minimum de code / de rendu

3. Améliorer les web vitals

App.jsx
import React from 'react';

import Navbar from '../Navbar';
import Shelf from '../Shelf';
import Filter from '../Shelf/Filter';
import FloatCart from '../FloatCart';

const App = () => (
  <>
    <Navbar />
    <main>
      <Filter />
      <Shelf />
    </main>
    <FloatCart />
  <>
);

export default App;
const Navbar = import('../Navbar');
const Shelf = import('../Shelf');
const Filter = import('../Shelf/Filter');
const FloatCart = import('../FloatCart');

3. Améliorer les web vitals

Code splitting

App.jsx
import React, { Suspense } from 'react';
import Loader from '../Loader';

const Navbar = React.lazy(() => import('../Navbar'));
const Shelf = React.lazy(() => import('../Shelf'));
const Filter = React.lazy(() => import('../Filter'));
const FloatCart = React.lazy(() => import('../FloatCart'));

const App = () => (
  <React.Fragment>
    <Suspense fallback={<Loader/>}>
      <Navbar />
    </Suspense>
      <main>
        <Suspense fallback={<Loader/>}>
          <Filter />
        </Suspense>
        <Suspense fallback={<Loader/>}>
          <Shelf />
        </Suspense>
      </main>
    <Suspense fallback={<Loader/>}>
      <FloatCart />
    </Suspense>
  </React.Fragment>
);

3. Améliorer les web vitals

Lazy Loading

3. Améliorer les web vitals

<link rel=”preload”>
<link rel=”prefetch”>

3. Améliorer les web vitals

Preload / prefetch

App.jsx
const Navbar = React.lazy(() => import(/* webpackChunkName: 'navbar' */ '../Navbar'));
const Shelf = React.lazy(() => import(/* webpackChunkName: 'shelf' */ '../Shelf'));
const Filter = React.lazy(() => import(/* webpackChunkName: 'filter' */ '../Shelf/Filter'));
const FloatCart = React.lazy(() => import(/* webpackChunkName: 'float-cart' */'../FloatCart'));
// ...
plugins: [
    // ...
    new PreloadPlugin({
        rel: 'preload',
    	include: ['navbar', 'shelf']
    )},
    new PreloadPlugin({
        rel: 'prefetch',
    	include: ['filter', 'float-cart']
    )},
],
webpack.config.js

3. Améliorer les web vitals

Preload / prefetch

3. Améliorer les web vitals

Preconnect

// ...
plugins: [
    // ...
    new HtmlWebpackPlugin({
      // ...
      preconnect: ['http://localhost:8081']
    }),
    new HtmlWebpackPreconnectPlugin(),
],
webpack.config.js

3. Améliorer les web vitals

Preconnect

// ...
plugins: [
    // ...
    new HtmlWebpackPlugin(),
    new HtmlInlineScriptPlugin([
      /runtime~.+[.]js$/,
      /app~.+[.]js$/
    ]),
],
webpack.config.js

3. Améliorer les web vitals

Inline JS et CSS

3. Améliorer les web vitals

AVANT / APRES

3. Améliorer les web vitals

1. Du vocabulaire

  • Les web vitals        
  • FCP, LCP, CLS...                         

 

📝 Take Away

✔️

✔️

2. Mesurer les web vitals

  • DevTools / lightouse-ci        
  • librairie web-vitals                   

 

✔️

✔️

3. Améliorer les web vitals

  • Utiliser des formats modernes
  • Minification + tree shaking
  • S'applique aussi au CSS
  • Compresser      

 

  • Code splitting + lazy loading    
  • Preload / prefetch
  • Preconnect
  • Penser à inline

✔️

✔️

✔️

✔️

✔️

✔️

✔️

✔️

Et plein d'autres choses...

stratégie de mise en cache (assets, app shell...)

lazy loading des images

HTTP2

cache réponses API

SSR

Performance ?

Conclusion

Conclusion

Merci !

 

🙏

#DevoxxFR

[Devoxx] Du make it work au make it fast : des quickwins pour booster votre application !

By Jonathan HERVIEUX

[Devoxx] Du make it work au make it fast : des quickwins pour booster votre application !

“Out of the box, webpack fait le café ! React c’est génial ; Uber, Airbnb, …, tout le monde l’utilise, avec ça le front de ton appli va être dingue !”. Ce sont plus ou moins les phrases que j’ai pu entendre auprès des équipes des quelques projets sur lesquels j’ai eu l’occasion de travailler, et c’est vrai, ça marche. Ça marche même très bien. Mais au fait, qu’est ce que ça signifie “ça marche” ? Pourquoi mon application affiche-t-elle systématiquement un loader à son démarrage ? Pourquoi la version web de reddit me transfère-t-elle 16Mo de data avec pas moins de 236 requêtes rien que sur la page d'accueil ? Est ce qu'un "web instantané" est possible ? Je vous propose plusieurs optimisations simples à mettre en place pour enfin maîtriser le contenu affiché sur vos applications web et ainsi bluffer vos futurs utilisateurs.

  • 241