Performance in React

https://slides.com/tylergraf/performance-in-react

Some Housekeeping Items

Tyler Graf

Senior Web Dev

FamilySearch

tyler@familysearch.org

@tylergraf

Focus: Page load performance

Ship
Less
Code

Rule 1:

Why?

Download Time

2G (280 Kbps)

3G (1.6 Mbps)

Cable (5 Mbps)

200 KB

(1 MB uncompressed)

5.7s

1.0s

320ms

Load Time Moto G4

2G (280 Kbps)

3G (1.6 Mbps)

Cable (5 Mbps)

200 KB

(1 MB uncompressed)

8.0s

3.3s

2.6s

http connection

SSL

| Device                    | Unique visitors in the last year | Perc  |
| ------------------------- | -------------------------------- | ----- |
| Apple iPhone              | 7,841,169                        | 25.2% |
| Apple iPad                | 3,280,247                        | 10.6% |
| Samsung Galaxy S8         | 684,974                          | 2.2%  |
| Samsung Galaxy S9         | 521,036                          | 1.7%  |
| Samsung Galaxy Note 8     | 428,127                          | 1.4%  |
| Samsung Galaxy S9 Plus    | 420,489                          | 1.4%  |
| Verizon Galaxy S7         | 345,610                          | 1.1%  |
| Samsung Galaxy S8 Plus    | 334,761                          | 1.1%  |
| Samsung Galaxy Tab A 10.1 | 235,683                          | 0.8%  |
| Samsung Galaxy Note 9     | 214,554                          | 0.7%  |
| Motorola Moto G5 Plus     | 206,180                          | 0.7%  |
| Google Pixel 2            | 182,505                          | 0.6%  |
| Google Pixel 2 XL         | 152,412                          | 0.5%  |
| Samsung Galaxy S8         | 143,763                          | 0.5%  |
| Samsung Galaxy S7         | 134,330                          | 0.4%  |
| Samsung Galaxy S7 Edge    | 134,220                          | 0.4%  |
| Unknown M3                | 133,875                          | 0.4%  |
| Motorola Moto G6          | 125,804                          | 0.4%  |
| AT&T Galaxy S7            | 121,423                          | 0.4%  |
| Motorola Moto G4          | 121,030                          | 0.4%  |
| Motorola Moto E4          | 118,282                          | 0.4%  |
| Amazon Fire HD 10         | 116,831                          | 0.4%  |
| Samsung Galaxy J7 Prime   | 116,220                          | 0.4%  |
| Amazon Fire HD 8          | 110,212                          | 0.4%  |
| Verizon Galaxy S6         | 109,875                          | 0.4%  |
| Verizon Galaxy S7 edge    | 107,282                          | 0.3%  |
| Samsung Galaxy Tab 4 10.1 | 104,458                          | 0.3%  |
| Samsung Galaxy Tab S2     | 102,892                          | 0.3%  |
| T-Mobile Galaxy S7        | 102,262                          | 0.3%  |
| Samsung Galaxy J2 Prime   | 101,905                          | 0.3%  |
| Samsung Galaxy A5         | 100,792                          | 0.3%  |
| Samsung Galaxy J5 Prime   | 98,201                           | 0.3%  |

Person Page

465KB

Person Page

465KB

Moto G4

3G (1.6/768)

Person Page

338KB

Moto G4

3G (1.6/768)

 

(after visiting the home page)

Person Page

38KB

Moto G4

3G (1.6/768)

 

(Cached View)

"[Christ] said those who work for the salvation of others will have their sins forgiven and will bring salvation to their own souls" (see D&C 4:4; 31:5; 84:61).

Real Why

Philippines 6 Mbps

China 3 Mbps

Peru 3 Mbps

Moto G4 - 3G

V7 - 3.2 MB (3167 KB)

V8 - 338 KB

How?

Be Lazy

Lazy Load Routes

Lazy Load Routes

import React, { Suspense, lazy } from "react";
import { Switch, Route, Router, NotFound } from "@fs/zion-router";

const PageOne = lazy(() => import("./components/pages/PageOne"));
const PageTwo = lazy(() => import("./components/pages/PageTwo"));
const PageThree = lazy(() => import("./components/pages/PageThree"));

export default function App() {
  return (
    <Suspense>
      <Router>
        <Switch>
          <Route exact path="/" component={PageOne} />
          <Route exact path="/two" component={PageTwo} />
          <Route exact path="/three" component={PageThree} />
          <Route component={NotFound} />
        </Switch>
      </Router>
    </Suspense>
  );
}

On-demand Loading

import React, { useState, Suspense, lazy } from "react";
import Person from "../components/Person";
import { Button } from "@fs/zion-ui";

const OverLay = lazy(() => import("../components/OverLay"));

export default function PageOne() {
  const [showMore, setShowMore] = useState();

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Person />
      <Button type="button" onClick={() => setShowMore(true)}>
        Show me more!
      </Button>
      {showMore && <OverLay />}
    </Suspense>
  );
}

When?

Not critical at render

Way below the fold

Content requires interaction

SHOW MORE

Showing more

Not All Things Are Equal

Funny Story

.icon.thing {
  background:
    url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7)
    no-repeat
    left center;
  padding: 5px 0 5px 25px;
}
.icon.thing2 {
  background:
    url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7)
    no-repeat
    left center;
  padding: 5px 0 5px 25px;
}
.icon.thing3 {
  background:
    url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7)
    no-repeat
    left center;
  padding: 5px 0 5px 25px;
}
.icon.thing4 {
  background:
    url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7)
    no-repeat
    left center;
  padding: 5px 0 5px 25px;
}

data:image * 100

2s faster!!

Treeshake

// maths.js
export function add(x,y) {
 return x + y
}

export function subtract(x,y) {
 return x - y
}
// main.js
import { add } from './maths';

add(10, 5)

What's Treeshaking?

Subtract won't be included in the bundle

Caveat

Doesn't work with commonjs modules.

// maths.js
exports.add = (x,y) => {
 return x + y
}

exports.subtract = (x,y) => {
 return x - y
}
// main.js
import { add } from './maths';

add(10, 5)

How do I know if a 3rd party module is tree-shakable?

Look at package.json main and module

{
  "name": "cool-package",
  "main": "./dist/main.js",
  "module": "./dist/main.es6.js"
}

Method 1

Webpack will look for module first, then main.

export function doStuff(){
  // do stuff
}

main.js, may use es6 modules as well, look at the source code.

Method 2

bundlephobia.com

Along with a bunch of other info like gzipped size, it includes tree-shakability.

Bundle analyzer

Be aware of packages and versions

Is that lodash?

61KB Total?

Demo

Do

Don't

  • Analyze what's in your bundle
  • Pay attention to percentages
  • Look for duplication
  • Pay attention to byte sizes as much
  • Assume everything gets loaded on every page

Audit Regularly

Synthetic

vs

RUM

  • Controlled
    • CPU
    • Network Conditions
  • Repeatable
  • Real
  • Histogram/Percentiles

RUM Histogram

Synthetic Monitoring

RUM

Real User Monitoring

PageStats (in house)

WebPageTest

How to set a session

setCookie     https://www.familysearch.org     fssessionid=82cc969a-787e-ca9f-8a06-b6093a54787f-prod
navigate     https://www.familysearch.org/tree/pedigree/landscape/LBGY-WZG
navigate     https://www.familysearch.org
setCookie     https://www.familysearch.org     fssessionid=82cc969a-787e-ca9f-8a06-b6093a54787f-prod
navigate     https://www.familysearch.org/tree/pedigree/landscape/LBGY-WZG

Homepage => Person Page

Person Page

Perf People to Follow

Addy Osmani

Google Dev Manager

@addyosmani

Alex Russell

Google Chrome Team

@slightlylate

Ilya Grigorik

Google Dev Advocate/WC3 Web perf

@igrigorik

Philippines 6 Mbps

China 3 Mbps

Peru 3 Mbps

Tyler Graf

Senior Web Dev

FamilySearch

tyler@familysearch.org

@tylergraf

https://slides.com/tylergraf/performance-in-react

Performance in React

By Tyler Graf

Performance in React

  • 870