Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Building JS bundles for React Native
Search
Rafael de Oleza
September 06, 2018
Programming
2
430
Building JS bundles for React Native
React Native EU 2018 talk
Rafael de Oleza
September 06, 2018
Tweet
Share
Other Decks in Programming
See All in Programming
AIコーディングAgentとの向き合い方
eycjur
0
270
プロパティベーステストによるUIテスト: LLMによるプロパティ定義生成でエッジケースを捉える
tetta_pdnt
0
1.7k
Ruby Parser progress report 2025
yui_knk
1
440
Android 16 × Jetpack Composeで縦書きテキストエディタを作ろう / Vertical Text Editor with Compose on Android 16
cc4966
2
230
意外と簡単!?フロントエンドでパスキー認証を実現する WebAuthn
teamlab
PRO
2
760
print("Hello, World")
eddie
2
530
Kiroで始めるAI-DLC
kaonash
2
590
testingを眺める
matumoto
1
140
今だからこそ入門する Server-Sent Events (SSE)
nearme_tech
PRO
3
230
🔨 小さなビルドシステムを作る
momeemt
4
680
GitHubとGitLabとAWS CodePipelineでCI/CDを組み比べてみた
satoshi256kbyte
4
240
時間軸から考えるTerraformを使う理由と留意点
fufuhu
16
4.8k
Featured
See All Featured
The Cost Of JavaScript in 2023
addyosmani
53
8.9k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
GraphQLとの向き合い方2022年版
quramy
49
14k
Rebuilding a faster, lazier Slack
samanthasiow
83
9.2k
Making the Leap to Tech Lead
cromwellryan
135
9.5k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.4k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
1.1k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.1k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Side Projects
sachag
455
43k
Testing 201, or: Great Expectations
jmmastey
45
7.7k
The Invisible Side of Design
smashingmag
301
51k
Transcript
Building JavaScript bundles for React Native Rafael Oleza @rafeca
What is Metro?
Why Metro • Fast • Scalable • Integrated
Anatomy of a JS bundle runtime module 1 ... module
n startup code
Anatomy of a JS bundle module 1 ... module n
startup code runtime • Register modules • Require modules
Anatomy of a JS bundle global.__d = function(factory, id) {
modules[id] = { factory, loaded: false, module: {exports: {}}, }; } module 1 ... module n startup code runtime
Anatomy of a JS bundle global.__r = function(id) { if
(!modules[id]) { throw new Error(`Module ${id} not found`); } const {module, factory, loaded} = modules[id]; if (loaded) { return module.exports; } modules[id].loaded = true; factory(global, global.__r, module, module.exports); return module.exports; } module 1 ... module n startup code runtime
Anatomy of a JS bundle module 1 ... module n
startup code runtime __d(function(global, require, module, exports) { 'use strict'; const foo = require(22); module.exports = foo; }, 1673);
Anatomy of a JS bundle module 1 ... module n
startup code runtime __d(function(global, require, module, exports) { 'use strict'; const foo = require(22); module.exports = foo; }, 1673);
Anatomy of a JS bundle module 1 ... module n
startup code runtime __d(function(global, require, module, exports) { 'use strict'; const foo = require(22); module.exports = foo; }, 1673);
Anatomy of a JS bundle module 1 ... module n
startup code runtime __d(function(global, require, module, exports) { 'use strict'; const foo = require(22); module.exports = foo; }, 1673);
Anatomy of a JS bundle module 1 ... module n
startup code runtime __d(function(global, require, module, exports) { 'use strict'; const foo = require(22); module.exports = foo; }, 1673);
Anatomy of a JS bundle module 1 ... module n
startup code runtime __r(0);
Anatomy of a JS bundle __r(0); (function(global) { const modules
= Object.create(null); global.__d = function(factory, id) { modules[id] = { factory, module: {exports: {}}, loaded: false, }; } global.__r = function(id) { if (!modules[id]) { throw new Error(`Module ${id} not found`); } const {module, factory, loaded} = modules[id]; if (loaded) { return module.exports; } modules[id].loaded = true; factory(global, global.__r, module, module.exports); return module.exports; } })(this); __d(function(global, require, module, exports) { const foo = require(1); module.exports = foo; }, 0); __d(function(global, require, module, exports) { module.exports = 'Hello, world'; }, 1);
__d(function(global, require, module, exports) { const foo = require(1); module.exports
= foo; }, 0); __d(function(global, require, module, exports) { module.exports = 'Hello, world'; }, 1); (function(global) { const modules = Object.create(null); global.__d = function(factory, id) { modules[id] = { factory, module: {exports: {}}, loaded: false, }; } global.__r = function(id) { if (!modules[id]) { throw new Error(`Module ${id} not found`); } const {module, factory, loaded} = modules[id]; if (loaded) { return module.exports; } modules[id].loaded = true; factory(global, global.__r, module, module.exports); return module.exports; } })(this); Anatomy of a JS bundle __r(0);
The bundling process
The bundling process 2 phases: • Build the Dependency Graph
• Graph Serialization
The bundling process 2 phases: • Build the Dependency Graph
• Graph Serialization
• Transform modules • Collect dependencies • Resolve dependencies Dependency
Graph
Dependency Graph Input: ./index.js import foo from './foo'; import leftPad
from 'left-pad'; // ...
Dependency Graph Input: Output: ./index.js import foo from './foo'; import
leftPad from 'left-pad'; // ... const foo = require('./foo'); const leftPad = require('left-pad'); // ...
Dependency Graph Input: Output: ./index.js import foo from './foo'; import
leftPad from 'left-pad'; // ... const foo = require(1); const leftPad = require(2); // ... [ "./foo", "left-pad", ]
Dependency Graph Input: Output: ./index.js import foo from './foo'; import
leftPad from 'left-pad'; // ... __d(function(global, require) { const foo = require(1); const bar = require(2); // ... }); [ "./foo", "left-pad", ]
Dependency Graph ./foo.js ./index.js Input: import foo from './foo'; import
leftPad from 'left-pad'; // ... Output: [ "./foo", "left-pad", ] __d(function(global, require) { const foo = require(1); const bar = require(2); // ... });
Dependency Graph ./node_modules/left-pad/index.js ./foo.js ./index.js Input: Output: [ "./foo", "left-pad",
] __d(function(global, require) { const foo = require(1); const bar = require(2); // ... }); import foo from './foo'; import leftPad from 'left-pad'; // ...
Dependency Graph ./node_modules/left-pad/index.js ./foo.js ./index.js ./bar.js ./baz.js
Dependency Graph ./node_modules/left-pad/index.js ./foo.js ./index.js ./bar.js ./baz.js
Dependency Graph Transforming files is slow • Parse JS into
AST • Mutate AST • Generate JS & SourceMaps from AST
1) Caching system • Extensible • Layered architecture class MyCacheStore
{ async get(key) { // Return value } async set(key, value) { // Set value } async clear() { // Clear all cached values } }
1) Caching system • Fulfilled by a CI job •
Makes initial builds considerably faster HTTP Remote cache
2) Parallelization jest-worker main.js import Worker from 'jest-worker'; async function
main() { const worker = new Worker('./worker.js'); const result = await worker.hello('Alice'); } worker.js export function hello(param) { return 'Hello, ' + param; }
3) Delta Bundler • Available in dev mode • Avoids
regenerating the Dependency Graph • Sends only changes to the devices • Enabled by default in Android (iOS soon!)
3) Delta Bundler ./bar.js ./foo.js ./index.js ./baz.js A ./index.js A
./foo.js A ./bar.js A ./baz.js
3) Delta Bundler ./bar.js ./foo.js ./index.js ./baz.js M ./foo.js
3) Delta Bundler ./bar.js ./foo.js ./index.js ./baz.js A ./new.js M
./bar.js ./new.js
3) Delta Bundler ./bar.js ./foo.js ./index.js ./baz.js M ./foo.js ./new.js
3) Delta Bundler ./bar.js ./foo.js ./index.js ./baz.js M ./foo.js M
./bar.js D ./new.js ./new.js
Scalable by design 1) Caching system 2) Parallelization 3) Delta
Bundler
The bundling process 2 phases: • Build the Dependency Graph
• Graph Serialization
Graph Serialization • Takes the graph object as an input
• Generates bundle from it
runtime module 1 ... module n startup code Plain JS
bundle
Random Access Modules bundle MOD_0_LENGTH MOD_0_OFFSET MOD_1_LENGTH MOD_1_OFFSET … 0xFB0BD1E5
NUM_MODULES MOD_0 \0 MOD_1 \0 START \0 … START_LENGTH 00 32 64 Header Table of contents List of modules
Random Access Modules bundle runtime module 1 ... module n
startup code JS VM RN standard bundle
JS VM RN Random Access Modules bundle runtime startup code
RAM bundle
Random Access Modules global.__r = function(id) { if (!modules[id]) {
throw new Error(`Module ${id} not found`); } // ... }
Random Access Modules global.__r = function(id) { if (!modules[id]) {
nativeRequire(id); } // ... } MOD_0_LEN MOD_0_OFF MOD_1_LEN MOD_1_OFF … 0xFB0BD1E5 NUM_MODULES MOD_0 \0 MOD_1 \0 START \0 … START_LEN 00 32 64
Random Access Modules More info: https://tinyurl.com/rambundles Reduced startup times Less
memory consumption
Inline requires const foo = require('./foo'); const leftPad = require('left-pad');
export default function sayHi() { const message = `Hi ${foo.getName()}`; return leftPad(message, 4); }
Inline requires export default function sayHi() { const message =
`Hi ${require('./foo').getName()}`; return require('left-pad')(message, 4); }
Inline requires const debugModule = require('./debug'); export default function sayHi()
{ if (__DEV__) { return debugModule(); } return 'Hi'; }
Inline requires const debugModule = require('./debug'); export default function sayHi()
{ return 'Hi'; }
Inline requires export default function sayHi() { if (__DEV__) {
return require('./debug')(); } return 'Hi'; }
Inline requires export default function sayHi() { return 'Hi'; }
Takeaways Slow build times? Use remote cache https://tinyurl.com/metrocaches Slow app
startup? Use RAM bundles https://tinyurl.com/enablerambundles
Thanks! https://github.com/facebook/metro