The Fastest Code
is the code you don't load
How not to get yelled at by Alex Russell
Smaller
Smarter
Overview
Tooling
- Tree shaking
- SSR
- Code splitting
Environment
- Less code
- Browser Devtools
- Browser APIs
- Network features
Smaller
Smaller Frameworks
Smaller Frameworks
Smarter
Environment Edition
Browser Devtools
- Inspector
- Lighthouse
Inspector: CSS+JS Coverage
Browser Devtools
Inspector: CSS Coverage
Browser Devtools
Lighthouse
Browser Devtools
Browser APIs
- Service Worker
- IntersectionObserver
Intersection Observer
Browser APIs
Intersection Observer: Native DOM API
Browser APIs
var iObserver = new IntersectionObserver((entries) => {
// If intersectionRatio is 0, the target is out of view
if (entries[0].intersectionRatio <= 0) return;
loadItems(10);
});
// start observing
iObserver.observe(
document.querySelector('.scrollerFooter')
);
Intersection Observer: react-intersection-observer
Browser APIs
import Observer from 'react-intersection-observer'
<Observer>
{(inView) => <h2>{`Header inside viewport ${inView}.`}</h2>}
</Observer>
Service Worker: offline-first pattern
Browser APIs
Service Worker: offline-first pattern
Browser APIs
Step 1: Configure plugin in webpack.config.js:
// webpack.config.js
var OfflinePlugin = require('offline-plugin');
module.exports = {
// ...
plugins: [
// ... other plugins
// Should usually be last
new OfflinePlugin({
...
})
]
}
Service Worker: offline-first pattern
Browser APIs
// main.entry.js
require('offline-plugin/runtime').install();
runtime.install({
onUpdateReady: () => {
// Tells to new SW to take control immediately
runtime.applyUpdate();
},
onUpdated: () => {
// Reload page to load the new version
window.location.reload();
},
onUpdateFailed: () => {
console.log('SW Event:', 'onUpdateFailed');
}
});
Step 2: Add the runtime into your entry file
Network features
- HTTP2
- Preload
Network features
H2
Network features
Server Push
https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/
Smarter
Tooling Edition
Code Splitting
- Per page
- Dynamic
Code splitting
Per entry-point
Dynamic - component-based
Code splitting
Code splitting
webpack-bundle-analyzer: stats.json
669kB
83kB
80kB
489kB
Code splitting
webpack-bundle-analyzer: gzipped
61kB
7kB
47kB
11kB
Code splitting
webpack-bundle-analyzer: parsed
151kB
48kB
209kB
25.4 kB
Code splitting: set-up
.babelrc
{
"presets": [
["env", {"modules": false}],
"react",
"stage-2"
],
"plugins": [
"transform-flow-strip-types"
],
"env": {
"browser": {
"presets": [
["env", {
"targets": {"chrome": 57, "firefox": 52, "safari": 10},
"modules": false,
"useBuiltIns": true
}],
"react",
"stage-2"
]
}
}
}
Code splitting: device-based
react-loadable
import Loadable from 'react-loadable';
const RadarLoader = Loadable({
loader: () => import(/* webpackChunkName: "radar" */ './radar'),
LoadingComponent: RadarSpinner
});
const RadarSpinner = ({ isLoading, error, pastDelay }: Props) => {
if (isLoading) {
return pastDelay ? <Spinner>Loading...</Spinner> : null;
} else if (error) {
return <div>Error! Component failed to load</div>;
}
return null;
};
export default class extends Component {
render() {
return (
<Container>
<Media query={d.mqs.tablet}>
{matches => (matches ? <RadarLoader /> : null)}
</Media>
</Container>
);
}
}
1
2
Code splitting: interaction-based
react-loadable
let Db = Loadable({
loader: () => import(/* webpackChunkName: "searchdb" */ './db'),
LoadingComponent: DbShim
});
class Search extends Component {
state = {initialised: false, query: ''};
loadDB = () => {
if (this.state.initialised) return;
this.setState({ initialised: true });
Database.preload();
};
updateQuery = (e) => this.setState({ query: e.target.value; });
render() {
const {query} = this.state;
const {cards, submitSearch} = this.props;
return (
<Container>
<Form query={query} onFocus={this.loadDB} onChange={this.updateQuery} />
<Db cards={cards} term={query} onTermUpdated={submitSearch} />
</Container>
);
}
}
3
1
2
Preload chunks
preload-webpack-plugin
Preload chunks
preload-webpack-plugin
<!DOCTYPE html>
<html lang="en">
<head>
...
<link rel="preload" as="script" href="/js/chunks/radar.js">
<link rel="preload" as="script" href="/js/chunks/root.js">
<link rel="preload" as="script" href="/js/chunks/searchdb.js">
<link rel="preload" as="script" href="/js/chunks/smoothscroll.js">
<style type="text/css">...</style>
<link href="/css/app.css" rel="preload" as="style" onload="this.rel='stylesheet'">
</head>
<body>...</body>
</html>
👇
Thanks!
@oliverturner
Presentation to MancReact May 2017
By Oliver Turner
Presentation to MancReact May 2017
A breakdown of the various tools and techniques we can use to slim down our payloads and keep our users (and Alex Russell) happy.
- 860