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

Made with Slides.com