I'm Miguel Ángel Durán

@midudev

youtube.com/midudev

#performance⚡️ #react ⚛️

#sushi 🍣 #travelling ✈️

Enabler Frontend at

👨‍💻

@midudev

What frontend library

do you use?

Text

@midudev

📷 🏠

@midudev

february 2019

@midudev

global score

@midudev

🏋️‍♂️

🏋️‍♂️

1.

2.

3.

4.

@midudev

TTI

february 2019

15 seconds

📹

@midudev

@midudev

february 2019

Is             slow?

We asked ourselves...

@midudev

today

february 2019

@midudev

@midudev

The problem is not React anymore

How do we have achieved this?

-2 seconds

-1 second

@midudev


Time to interactive

Getting the most out of performance 🚀
while keeping bots happy 🤖

rendering strategies

@midudev

the problem

Understanding

@midudev

the problem

is...

render is expensive

and

@midudev

specially on the client 📱

Server Side Rendering (SSR)

Slow Time to First Byte

Maintain different codebases

@midudev

Time To Interactive

First Contentful Paint

SEO Friendly

Static Rendering

TTI & FCP

Fast TTFB!

Static files

👀 hydratation could be needed

export

@midudev

Inflexible

Client Side Rendering

Flexible

Fast TTFB and FCP!

Just static files

TTI way after FCP

No SEO friendly

@midudev

ReactDOM.render(
  <App />,
  document.getElementById('root')
)

Client Side Rendering

with

trying to get the best of two worlds...

SSR with (re)hydration 💦

@midudev

SSR 🌍 + CSR 🌎

SSR with (re)hydration 💦

hydration data

html from server

}

@midudev

server side

@midudev

const initialData = await getInitialData()
const html = ReactDOM.renderToString(
  <App initialData={initialData} />
)

res.send(`
  <div id='root'>${html}</div>
  <script>
    window.__INITIAL_DATA__ = ${JSON.stringify(initialData)}
  </script>`
)

with

SSR with (re)hydration 💦

ReactDOM.hydrate(
  <App initialData={window.__INITIAL_DATA__} />,
  document.getElementById('root')
)
// SERVER SIDE simplified
// CLIENT SIDE simplified

🏁 Attach event listeners to the existing markup

💦 Hydration

🔎 Check mismatches between server-side and client-side

🌳 Recreate tree on the virtual dom

🚀 Set up life cycles order and fire them

a.k.a bootstraping or initialization

⚛️ constructor, componentDidMount, useEffect, ...

ReactDOM.hydrate
app.$mount('#root')

@midudev

SSR with (re)hydration 💦

}

ReactDOM.hydrate

from server

client after hydration

@midudev

on client side

frameworks

@midudev

SSR with (re)hydration 💦

Flexible

Universal code

Fast TTFB and FCP

SEO Friendly

@midudev

SSR with (re)hydration 💦

the best of the two worlds?

Slow TTFB from SSR

TTI way after FCP from CSR

@midudev

SSR with (re)hydration 💦

... or the worst?

SSR

with (re)hydration

DEMO

@midudev

@midudev

a solution

Finding

@midudev

but... how?

🤖 SEO

I still want to...

⚛️ Flexibility

🤳 User Experience (UX)

👩‍💻 Developer Experience (DX)

@midudev

rendering strategies

@midudev

Dynamic Rendering

starting with...

@midudev

Dynamic Rendering

what is...

🤖

👫

@midudev

Strategies with

Dynamic Rendering

by Route

by Component

@midudev

Dynamic

Rendering

by route

@midudev

// 🚇 middleware to activate Dynamic Rendering on Express
const BOTS_USER_AGENTS = [
  'googlebot',
  'mediapartners-google',
  'yandexbot'
]

const INDEX_HTML_PATH = path.join(__dirname, 'public', 'index.html')
const HTML_TEMPLATE = fs.readFileSync(INDEX_HTML_PATH, 'utf8')

module.exports = function middleware(req, res, next) {
  const rawUserAgent = req.get('user-agent')
  const userAgent = rawUserAgent.toLowerCase()

  // check if the request comes from a bot 🤖
  if (BOTS_USER_AGENTS.find(ua => userAgent.includes(ua))) {
    // if not an user, just use the normal behaviour (SSR?)
    return next()
  }

  // return index.html directly if is an user 👩‍💻
  return res.send(HTML_TEMPLATE)
}
// 🚆 Using the middleware in Express
const dynamicRendering = require('./dynamic-rendering')

// Use dynamic rendering for a specific path for testing
app.get(
    '/es/:op/:type/barcelona-capital/*',
    dynamicRenderingExperiment
)




// Isomorphic routes handler
app.get('*', serverSideRenderingMiddleware)
next()
response html

👩‍💻

🤖

Dynamic

Rendering

at route level

DEMO

@midudev

@midudev

Dynamic

Rendering

by route

♻️ Fast TTFB

🧹 Hydration data is gone

1️⃣ Only changes on the server side

🧪 Useful technique for perf experiments

🆓 Free resources from your server

⏳ SEO Friendly

🤔 Client Side Rendering for user

🚩 Bad first paint

@midudev

Is Dynamic Rendering worth it?

🤔🚩

so...

@midudev

Dynamic

Rendering

at component level

@midudev

ServerSide Rendering

with (re)hydration

⚛️

@midudev

Dynamic Rendering

at component level

with

...

@midudev

⚛️

🤖 🌍

💁‍♂️ 🌍

👀

👀

@midudev

viewport

How to use & implementation

@midudev

<DynamicRendering userAgent={universalUserAgent}>
    <a href='https://very-interesting-url.com'>
        <VeryComplexToComputeComponent />
        <img src='https://huge-image.com/panda.jpg' />
    </a>
</DynamicRendering>

@schibstedspain/react-perf-dynamic-rendering
http://bit.ly/sui-dynamic-rendering

// ⚛️ <DynamicRendering /> ~"kinda" implementation

export default function DynamicRendering({ children, userAgent }) {

  const isBot = checkUserAgentIsBot({userAgent})
  if (isBot) return children

  if (typeof window !== 'undefined') { // isBrowser
    return <LazyContent>{children}</LazyContent>
  } else { // isServer
    return <div style={{border: '1px solid red'}} />
  }
 
}

A real use case scenario

@midudev

in production

📏 ~700px visible

>20000px rendered 👉

😱

👀 only 1 card

34 cards

@midudev

without Dynamic Rendering

~525ms

with Dynamic Rendering

~107ms

-80% reduction ✂️

@midudev

Only 1️⃣ requirement:

It's open source!

you need to use React ⚛️

# npm
npm install @midudev/react-dynamic-rendering

# yarn
yarn add @midudev/react-dynamic-rendering

Fully compatible with:

@midudev

📦

@midudev

Dynamic

Rendering

at component level

DEMO

@midudev

@midudev

Dynamic

Rendering

at component level

✅ Improve TTI

😴 Lazy Load for the user

🏋️‍♀️ Perfect for stuff below the fold

🆓 Free resources from your server

⏳ Help GoogleBot to index your content faster

🚩 Keep Hydration data

👩‍🔬 Need universal UA calculation

🤖 Bot still gets full cost

 

@midudev

Does              like it?

I hope it does.

It's their idea! 💡

@midudev

The new bots of Google are superb!

🧩 JS Modules

📐 Intersection Observer

🧱 Custom Elements

🌚 Shadow DOM

🏫 Classes

🏷 Tagged Template Literals

Why do we need this?

@midudev

links detected

at a later time

💸 expensive!

https://youtu.be/LXF8bM4g-J4

130 trillion web pages to crawl

rendering all those pages, takes time

WRS

Google Bot 101

@midudev

want/need to be
crawled fast and indexed faster
use server-side rendering. period.

tl;dl 👂

@midudev

also SSR is usually faster than CSR

@midudev

@midudev

Static

Rendering

at component level

@midudev

🏁 Attach event listeners to the existing markup

💦 Hydration

🔎 Check mismatches between server-side and client-side

🌳 Recreate tree on the virtual dom

🚀 Set up life cycles order and fire them

a.k.a bootstraping or initialization

constructor, componentDidMount, useEffect, ...

avoid the cost of this 👆

@midudev

How to use & implementation

@midudev

// ⚛️ <StaticContent /> usage
import StaticContent from './StaticContent'

function Footer () {
  return (
    <StaticContent>
      <HugeListOfLinks data={listOfLinks} />
    </StaticContent>
  )
}
// ⚛️ <StaticContent /> ~implementation
import React from 'react'

export default function StaticContent({children}) {
  // we're in the server, just render the content
  if (typeof window === 'undefined') {
    return <div>{children}</div>
  }

  // avoid re-render on the client
  return (
    <div
      dangerouslySetInnerHTML={{__html: ''}}
      suppressHydrationWarning
    />
  )
}

⚛️ core contributor

@midudev

@midudev

Static

Rendering

DEMO

@midudev

@midudev

Static

Rendering

at component level

 Avoid re-hydrate for static components

🤳 Thus could greatly improve TTI

📸 For expensive rendering lists or static content (SEO Footers)

🤖 GoogleBot is definitely going to detect it

⚠️ Lose interactivity

🏋️‍♀️ Hydration data still there

🥪 Element wrapper (ex. <div>)

@midudev

Only 1️⃣ requirement:

It's open source!

you need to use React ⚛️

# npm
npm install @midudev/react-static-content

# yarn
yarn add @midudev/react-static-content

Fully compatible with:

@midudev

Can we do it better?

Can we do it progressive?

@midudev

@midudev

Static

Rendering

Dynamic

Rendering

at component level

@midudev

Progressive

Hydration

@midudev

👀

👀

👀

👈 Static HTML

👈 Static HTML

👈 Static HTML

@midudev

@schibstedspain/react-perf-dynamic-rendering
http://bit.ly/sui-dynamic-rendering

// ⚛️ How to use <ProgressiveRendering />

function ProgressiveHydrationUsage({articles}) {
  return (
    <Grid>
      <h1>Articles</h1>
      {articles.map(article => (
        <ProgressiveHydration key={article.id}>
          <Card {...article} />
        </ProgressiveHydration>
      ))}
    </Grid>
  )
}

@schibstedspain/react-perf-dynamic-rendering
http://bit.ly/sui-dynamic-rendering

// ⚛️ <ProgressiveRendering /> ~"kinda" implementation
function ProgressiveHydration({children}) {
  const ref = useRef(null)
  const isNearScreen = useNearScreen({ref})

  useEffect(() => {
    // CLIENT: If the element is near screen, then hydrate it
    if (isNearScreen) {
      ReactDOM.hydrate(children, ref.current)
    }
  }, [children, isNearScreen])

  // SERVER: Just render the content as usual
  if (typeof window === 'undefined')
    return <div ref={ref}>{children}</div>

  // CLIENT: Avoid hydration until we say so
  return (
    <div ref={ref}
      suppressHydrationWarning
      dangerouslySetInnerHTML={{__html: ''}}
    />
  )
}

vue-lazy-hydration by maoberlehner

Lazy hydration of server-side rendered Vue.js components

about

lazy-hydration by znck

Lazy Hydration for Vue SSR

Simple API. Plug and play.
Configurable interaction to fire hydration.

@midudev

<template>
  <div class="ArticlePage">
    <LazyHydrate when-idle>
      <ImageSlider/>
    </LazyHydrate>

    <LazyHydrate ssr-only>
      <ArticleContent :content="article.content"/>
    </LazyHydrate>

    <LazyHydrate when-visible>
      <AdSlider/>
    </LazyHydrate>

    <!-- `on-interaction` listens for a `focus` by default ... -->
    <LazyHydrate on-interaction>
      <CommentForm :article-id="article.id"/>
    </LazyHydrate>

    <!-- ... but you can listen for any event you want ... -->
    <LazyHydrate on-interaction="click">
      <CommentForm :article-id="article.id"/>
    </LazyHydrate>

    <!-- ... or even multiple events. -->
    <LazyHydrate :on-interaction="['click', 'touchstart']">
      <CommentForm :article-id="article.id"/>
    </LazyHydrate>
  </div>
</template>

vue-lazy-hydration

about

@midudev

and

react-prerendered-component by theKashey

Partial Hydration and Component Level Caching
Powerful 💪 but complicated 🤹‍♂️

react-progressive-hydration by

Progressive Hydration
Experimental 🧪 but interesting 🤯

@midudev/react-progressive-hydration

Progressive Hydration
Breaks connectivity between React trees ⚛️ 🌳

@midudev

Progressive

Hydration

DEMO

@midudev

@midudev

👀 Only re-hydrate what's visible

🤳 Thus could greatly improve TTI

🔛 Activate interactivity on demand

📸 Kind of lazy loading experience

🤖 GoogleBot will get the rendered static html (not hydrated)

🏋️‍♀️ Hydration data still there

🥪 Element wrapper (ex. <div>)

⚠️ No Context passed down

Progressive

Hydration

@midudev

Only 1️⃣ requirement:

It's open source!

you need to use React ⚛️

# npm
npm install @midudev/react-progressive-hydration

# yarn
yarn add @midudev/react-progressive-hydration

Fully compatible with:

@midudev

so, what's next?

@midudev

SSR in React could be much better

from Andrew Clarke presentation: https://youtu.be/z-6JC0_cOns?t=950

Can't start rendering anything until everything is loaded

Encapsulation is difficult (no state or lifecycles)

Need to hoist all async data

@midudev

First class support

on React?

@midudev

Progressive Hydration
Selective Hydration

Yuzhi Zheng at React Conf 2019

@midudev

Actual SSR with hydration

@midudev

hydrate on the client 💦
while streaming from the server 📡

Future SSR with hydration

📹

@midudev

Selective Hydration

@midudev

so...

@midudev

How have we achieved this?

Static

Rendering

Dynamic

Rendering

@midudev


Time to interactive

is could be slow.

Now you have

strategies to fix it.

@midudev

rendering strategies

@midudev

¡Gracias Alicante 🙇‍♂️!

all links &
resources

@midudev

DevFest Alicante - React Rendering Strategies

By Miguel Angel Durán García

DevFest Alicante - React Rendering Strategies

Version: DevFest Alicante

  • 484

More from Miguel Angel Durán García