I'm Miguel Ángel Durán

@midudev

Enabler Frontend at

 

 

 

youtube.com/midudev

Why are we here?

february 2019

users trying to interact with your site

TTI:

february 2019

15 seconds

               is SLOW, you definitely can't improve more the performance. It's IMPOSSIBLE.

You MUST move to Pug to FIX this. 

Or even better RETURN TO .NET TO SEND HTML.

twitter developer

today

How have we achieved this?

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

rendering strategies

the problem

Understanding

the problem

is...

rendering stuff on the client is expensive

and

quick recap
rendering on the web

Server Side Rendering (SSR)

👍 TTI & FCP
 

👎 Slow TTFB

👎 Interactive with different language
      so... hard to mantain

Static Rendering

👍 TTI & FCP

👍 Fast TTFB!

👍 Static files

 

👎 Inflexible

👀 hydratation could be needed

export

Client Side Rendering

👍 Flexible

👍 Fast TTFB and FCP!

👍 Just one static index.html

 

👎 Slow TTFB

👎 TTI way after FCP

trying to get the best of two worlds

SSR 🌍 + CSR 🌎

SSR with (re)hydration

finally

that's (re)hydration
and it looks like this...

SSR with (re)hydration

👍 Flexible

👍 Universal code

👍 Fast TTFB and FCP


👎 Slow TTFB

👎 TTI way after FCP

👎 Infrastructure💰

someone in the audience that uses        ...
but doesn't want to say it

SSR with (re)hydration


is getting the best...

or the worst of both?

 

 

👍 Flexible

👍 Universal code

👍 Fast TTFB and FCP


👎 Slow TTFB

👎 TTI way after FCP

👎 Infrastructure💰

SSR

with (re)hydration

DEMO

a solution

Finding

solution

and the

is...

render less things

on the client and/or the server

but... how?

🤖 SEO

I still want to...

⚛️ Flexibility

🤳 UX

👩‍💻 DX

rendering strategies

Dynamic Rendering

starting with...

Dynamic Rendering

wt* is...

🤖

👫

Does              like it?

I hope it does.

It's their idea! 💡

Strategies with

Dynamic Rendering

by Route

by Component

Dynamic

Rendering

by route

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

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

export default 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('*', SSR)

Dynamic

Rendering

by route

♻️ TTFB

🧹 Hydration data is gone

🧪 Useful technique for perf experiments

🆓 Free resources from your server

⏳ Help GoogleBot to index your content faster

☝️ More about this, later 👉

🤔 Just a CSR for the user

🚩 FP & FCP

Dynamic

Rendering

at component level

// ⚛️ Using <DynamicRendering /> component 
// userAgent must be retrieved universally
// on server and client

<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 /> kind of implementation

export default function DynamicRendering(props) {
  const { children, disabled, forceRender, height, userAgent } = props

  // check if the userAgent is a bot and if we're in the browser
  const isBot = checkUserAgentIsBot({userAgent})
  const isOnBrowser = typeof window !== 'undefined'

  // Force render in server and client
  if (forceRender) return children

  // if isBot or disabled, we return in server and client the content
  if (isBot) return children

  // now, we're sure the user isNotBot
  // so check if we're on the browser side and
  // if is not disabled the component
  if (isOnBrowser && !disabled) {
    return <LazyContent height={height}>{children}</LazyContent>
  } else {
    // so, we're on the server side or the component is disabled
    return <div style={{height: `${height}px`}} />
  }
}

👈 ~700px visible

~20000px rendered 👉

😱

without Dynamic Rendering

~500ms

with Dynamic Rendering

~100ms

5x times less

@schibstedspain/react-perf-dynamic-rendering

🔗 http://bit.ly/sui-dynamic-rendering

Only 1️⃣ requirement:

It's open source!

you need to use React ⚛️

Dynamic

Rendering

at component level

DEMO

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

☝️ More about this, now 👉

🚩 Keep Hydration data

👩‍🔬 Need universal UA calculation

🤖 Bot still gets full cost

 

The new GoogleBot is superb!

🧩 JS Modules

📐 Intersection Observer

🧱 Custom Elements

🌚 Shadow DOM

🏫 Classes

🏷 Tagged Template Literals

Why do we need this?

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

https://youtu.be/LXF8bM4g-J4

If you want/need your site

to be crawled fast and indexed faster

you better bet on SSR

tl;dl 👂

for now

also SSR is usually faster than CSR

Static

Rendering

at component level

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

function Footer () {
  return (
    <StaticContent>
      <HugeListOfLinks data={listOfLinks} />
    </StaticContent>
  )
}
// ⚛️ <StaticContent /> easy-peasy 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
      suppressHydrationWarning
      dangerouslySetInnerHTML={{__html: ''}}
    />
  )
}

Static

Rendering

DEMO

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>)

Progressive

Rendering

a.k.a partial hydration

Static

Rendering

at component level

Dynamic

Rendering

at component level

🏩

Progressive

Rendering

a.k.a partial hydration

DEMO

👀 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>)

Progressive

Rendering

a.k.a partial hydration

so, what's next?

How have we achieved this?

Static

Rendering

Dynamic

Rendering

is could be slow.

Now you have strategies

to fix it.

rendering demos

Thanks!

gifs borrowed from @eh_cat!

@midudev

youtube.com/midudev

Made with Slides.com