Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building JS bundles for React Native

Building JS bundles for React Native

React Native EU 2018 talk

Rafael de Oleza

September 06, 2018
Tweet

Transcript

  1. Anatomy of a JS bundle module 1 ... module n

    startup code runtime • Register modules • Require modules
  2. Anatomy of a JS bundle global.__d = function(factory, id) {

    modules[id] = { factory, loaded: false, module: {exports: {}}, }; } module 1 ... module n startup code runtime
  3. 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
  4. 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);
  5. 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);
  6. 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);
  7. 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);
  8. 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);
  9. Anatomy of a JS bundle module 1 ... module n

    startup code runtime __r(0);
  10. 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);
  11. __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);
  12. Dependency Graph Input: Output: ./index.js import foo from './foo'; import

    leftPad from 'left-pad'; // ... const foo = require('./foo'); const leftPad = require('left-pad'); // ...
  13. 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", ]
  14. 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", ]
  15. 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); // ... });
  16. 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'; // ...
  17. Dependency Graph Transforming files is slow • Parse JS into

    AST • Mutate AST • Generate JS & SourceMaps from AST
  18. 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 } }
  19. 1) Caching system • Fulfilled by a CI job •

    Makes initial builds considerably faster HTTP Remote cache
  20. 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; }
  21. 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!)
  22. 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
  23. Random Access Modules bundle runtime module 1 ... module n

    startup code JS VM RN standard bundle
  24. Random Access Modules global.__r = function(id) { if (!modules[id]) {

    throw new Error(`Module ${id} not found`); } // ... }
  25. 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
  26. Inline requires const foo = require('./foo'); const leftPad = require('left-pad');

    export default function sayHi() { const message = `Hi ${foo.getName()}`; return leftPad(message, 4); }
  27. Inline requires export default function sayHi() { const message =

    `Hi ${require('./foo').getName()}`; return require('left-pad')(message, 4); }
  28. Inline requires const debugModule = require('./debug'); export default function sayHi()

    { if (__DEV__) { return debugModule(); } return 'Hi'; }
  29. Inline requires export default function sayHi() { if (__DEV__) {

    return require('./debug')(); } return 'Hi'; }
  30. Takeaways Slow build times? Use remote cache https://tinyurl.com/metrocaches Slow app

    startup? Use RAM bundles https://tinyurl.com/enablerambundles