https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1502316a-46ba-4d36-b108-767ed686e124/Babel_RN_EN_(2)_(1).png

My PR, which improves Babel's Import Performance, was included in 7.6.0 👏

Updating Babel will improve React Native's startup time without any changes. Let me tell you what happened. There's no interesting content, but please enjoy it.

# The change starts with a small interest!

The code we write on the React Native platform goes through Babel and Metro into one large JavaScript file (bundle). Sometimes, There is a day when I want to look into how this file was generated. That's because it's very interesting to find poorly generated code and think about why it was not intended. Please try it too.

How to look at the bundle file

If you execute react-native bundle in the project root directory as shown below, Metro will bundle the code compiled with Babel into the index.android.js file.

$ mkdir android/app/src/main/assets
$ react-native bundle --platform android --dev true --entry-file index.js --bundle-output android/app/src/main/assets/index.android.js --assets-dest android/app/src/main/res/

Looking at the import logic and discovering inefficiency.

import React, { PureComponent } from 'react';

This above code is used as a template when creating a new class component. In React Native, wildcard import is converted as follows.

var _interopRequireWildcard = _$$_REQUIRE(_dependencyMap[1], "@babel/runtime/helpers/interopRequireWildcard");
var _react = _interopRequireWildcard(_$$_REQUIRE(_dependencyMap[7], "react"));

@babel/runtime/helpers/interopRequireWildcard.js is as follows.

function _interopRequireWildcard(obj) {
  if (obj && obj.__esModule) {
    return obj;
  } else {
    var newObj = {};

    if (obj != null) {
      for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
          var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {};

          if (desc.get || desc.set) {
            Object.defineProperty(newObj, key, desc);
          } else {
            newObj[key] = obj[key];
          }
        }
      }
    }

    newObj["default"] = obj;
    return newObj;
  }
}

module.exports = _interopRequireWildcard;

Since react module is not an ES format module, it takes the logic of the else clause, and the problem is that it iterates over for loop and checks the property and puts the value in the newly created newObj object. In case of react, 29 loops are executed per call import. As a result, it's executed 29,000 times if used in 1,000 components. This seems too inefficient.

# Let's put the cache logic!

We can cache the final object created through the for loop for each module. Then, even if we use 1,000 components, only the first 29 loop iterations will execute for the react module. You can assume that the cost of processing import is close to zero. Now it looks a bit efficient :)

The logic has changed as shown below.

**// Function implementation for singleton cache processing**
function _getRequireWildcardCache() {
  if (typeof WeakMap !== "function") return null;
  var cache = new WeakMap();
  _getRequireWildcardCache = function () { return cache; };
  return cache;
}

function _interopRequireWildcard(obj) {
  if (obj && obj.__esModule) {
    return obj;
  }

  **// Returns stored value if cache exists**
  var cache = _getRequireWildcardCache();
  if (cache && cache.has(obj)) {
    return cache.get(obj);
  }

  var newObj = {};
  if (obj != null) {
    **// Improved redundancy checking for use of defineProperty**
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        var desc = hasPropertyDescriptor
          ? Object.getOwnPropertyDescriptor(obj, key)
          : null;
        if (desc && (desc.get || desc.set)) {
          Object.defineProperty(newObj, key, desc);
        } else {
          newObj[key] = obj[key];
        }
      }
    }
  }
  newObj.default = obj;

  **// Puts created object in cache**
  if (cache) {
    cache.set(obj, newObj);
  }
  return newObj;
}

module.exports = _interopRequireWildcard;