/pərˈfôrməns/
noun
səkˈses/
noun
*Diagram courtesy of Google (Paul Lewis & Paul Irish), "Introduction to RAIL (Chrome Dev Summit 2015)"
⌛️⌛️⏳
⏳
😭
😁
*Diagram courtesy of Google (Paul Lewis & Paul Irish), "Introduction to RAIL (Chrome Dev Summit 2015)"
*Diagram courtesy of Google (Paul Lewis & Paul Irish), "Introduction to RAIL (Chrome Dev Summit 2015)"
Applicable to inputs:
Goal: Visual response to click/tap in < 100ms
Where the connection between action and reaction breaks.
🛠 Implementation Strategies:
🛠 Implementation Strategies:
Goal: Each frame completes in less than 16ms (60 fps)
*Diagram courtesy of Google (Paul Lewis), "Rendering Performance" - developers.google.com
🛠 Implementation Strategies:
**For animations experiencing sub-optimal performance:
.moving-element {
will-change: transform;
}
.moving-element {
transform: translateZ(0);
}
⚠️ Don't overuse
🛠 Implementation Strategies:
Goal: Use idle time to do work, while still satisfying the Response goal
// Creating a new Web Worker
const myWorker = new Worker("./worker.js");
// Giving work to the worker thread
myWorker.postMessage(arbitraryObject);
console.log("Message posted to worker");
main.js
Implementation Strategies (freeing up the main thread):
Web Workers
🛠
Web Workers
...
// Handling result event from worker thread
myWorker.onmessage = function(e) {
console.log(`
Message received from worker:
${JSON.stringify(e.data)}
`);
}
main.js
onmessage = function(e) {
console.log(`
Received object from the main script:
${JSON.stringify(e.data)}
`);
const workerResult = doWork(e.data);
// Posting a message back to main.js
postMessage(workerResult);
}
worker.js
Other Implementation Strategies (freeing up the main thread):
🛠
Goal: First meaningful paint in < 1000ms
🛠 Implementation Strategies
class View extends React.Component {
...
// This method is invoked immediately after
// a component is mounted to the DOM.
componentDidMount() {
fetch("my-endpoint")
.then(data => data.json())
.then(json => this.setState({ pageData: json }))
.catch(e => this.setState({ pageError: e }));
}
render() {
return (
<MostMeaningfulContent />
{this.state.pageData && <SupplementalContent />}
)
}
}
🛠 Implementation Strategies
shouldComponentUpdate()
React.PureComponent
shouldComponentUpdate(nextProps, nextState) {
return true;
}
Only needed if render() is a known bottleneck: - Requires hardcoding at the prop/state level - Has its own performance cost - Complicates the workflow (immutability)
DOM
Virtual DOM
<App />
<Header />
<Body />
New Props or State
render()
<Title />
<TileList />
<Tile />
<Tile />
<Tile />
render()
render()
render()
render()
render()
render()
render()
render()
Diff?
Update DOM()
class Greeting extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
const { props, state } = this;
if(
nextProps.name !== props.name
|| nextProps.hasVisited !== props.hasVisited
) {
// Update the component if a prop has changed
return true;
} else {
return false;
}
}
render() { // After initial render, only called if
// shouldComponentUpdate returns true
const { name, hasVisited } = this.props;
return <h1>Welcome {hasVisited && "back "}{name}!</h1>;
}
}
import Perf from 'react-addons-perf';
class Thing extends React.PureComponent {
...
}
class Thing extends React.PureComponent {
...
}
Only needed if render() is a known bottleneck:- Requires hardcoding at the prop level- Has its own performance cost - Complicates the workflow (immutability)
class Thing extends React.PureComponent {
...
}
class Thing extends React.Component {
...
shouldComponentUpdate(nextProps, nextState) {
let shouldUpdate = false;
Object.keys(nextProps).map(prop => {
if (nextProps[prop] !== this.props[prop]) {
// A prop has shallowly changed
shouldUpdate = true;
}
});
Object.keys(nextState).map(element => {
if (nextState[element] !== this.state[element]) {
// An element of state has shallowly changed
shouldUpdate = true;
}
});
return shouldUpdate;
}
}
O(n^2) ➡ 🏌 ➡ O(nlogn) ➡ 🏌 ➡ O(n)
// Turning an Array of objects into a hash for O(1) operations.
// Illustrated below: If the property being hashed by ('id' here)
// is not unique (like a primary key), previous records with the same
// id will be overwritten.
const myArr = [
{id: 1, data: 'a'},
{id: 1, data: 'b'},
{id: 2, data: 'c'}
];
const objectOfObjects = myArr.reduce((newHash, record) => {
newHash[record.id] = record;
return newHash;
}, {}) // <- {} is the 'newHash' seed
console.log(objectOfObjects);
RAIL Model
https://developers.google.com/web/fundamentals/performance/rail
https://www.smashingmagazine.com/2015/10/rail-user-centric-model-performance/
Web App Performance High Level Strategies
https://developers.google.com/web/fundamentals/performance/rendering/
https://www.smashingmagazine.com/2016/11/true-lies-of-optimistic-user-interfaces/
Web App Performance Techniques
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
Optimizing Web App Animations
https://developers.google.com/web/fundamentals/design-and-ux/animations/
https://developers.google.com/web/fundamentals/design-and-ux/animations/animations-and-performance
https://medium.com/outsystems-experts/how-to-achieve-60-fps-animations-with-css3-db7b98610108
https://developers.google.com/web/fundamentals/performance/rendering/stick-to-compositor-only-properties-and-manage-layer-count
React Performance Techniques