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

Other Decks in Programming

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