The React Native production package filters out test code

Conclusion: It is possible to filter out some of the test pages when producing the production environment code package using the __DEV__ variable provided in React Native.

Environment Description:

"React" : "16.8.3", "the react - native" : "0.59.3."Copy the code

The problem

Our RN directory looks something like this:

./ ├─ Common Code Directory ├─ entry ├─ index.js ├─ Page Code DirectoryCopy the code

In our RN code, there are some common components, such as Button LoadingDialog, placed in the common directory. Component development, usually in the page, add a corresponding test page, for example to test the LoadingDialog, tend to add a page/test/loadingTest TSX composite file, in this page, you can test the components of all sorts of function is OK.

In page/index.ts, there will be a tool that automatically scans the page/**/**.tsx to generate all the page information, of course, it will also contain a lot of test pages used in development.

The contents of this page/index.ts are as follows:

import accountBindCardBindCard from './account/bindCard/BindCard';
import testMainMain from './test/main/Main';
import testMarqueeMarquee from './test/marquee/Marquee';
import testStyleTestLoading from './test/styleTest/Loading';
import testStyleTestModal from './test/styleTest/Modal';
import testStyleTestTextAnimation from './test/styleTest/TextAnimation';
import testStyleTestTextTest from './test/styleTest/TextTest';
import testStyleTestWithdrawResult from './test/styleTest/WithdrawResult';
import uplanInvestTypeInvestType from './uplan/investType/InvestType';
import uplanJoinJoin from './uplan/join/Join';

export default {
    'account/bindCard/BindCard': accountBindCardBindCard,
    'Main': testMainMain,
  	 // The following pages in the test directory are all test pages
    'test/marquee/Marquee': testMarqueeMarquee,
    'test/styleTest/Loading': testStyleTestLoading,
    'test/styleTest/Modal': testStyleTestModal,
    'test/styleTest/TextAnimation': testStyleTestTextAnimation,
    'test/styleTest/TextTest': testStyleTestTextTest,
    'test/styleTest/WithdrawResult': testStyleTestWithdrawResult,
     // The goal is to reduce the size of the production package by killing the test pages
    'uplan/investType/InvestType': uplanInvestTypeInvestType,
    'uplan/join/Join': uplanJoinJoin,
};
Copy the code

This script will scan the page directory first. If the production mode is –dev false, then generate the contents of the page/index.ts file. Pages in the page/test directory are filtered out.

In the next RN upgrade, we are going to kill our JS scripts and go back to calling RN’s official package scripts.

Therefore, there needs to be a way that allows us to selectively load certain pages in development and production environments.

use__DEV__

As you can easily imagine, RN officially provides a global variable, __DEV__, to indicate whether the current production mode is present.

The idea is simple: instead of importing test pages directly from page/index.ts, you need to import them based on __DEV__. However, in the actual testing process, I still found some problems. Finally, I tested the following situations:

export default {
    get 'account/bindCard/BindCard'() { return require('./account/bindCard/BindCard').default; },
    // There are several ways to write it
    get 'test/story/StoryDisplay'() {
        if (__DEV__) {
            return require('./test/story/StoryDisplay').default;
        }
        return null;
    },
    get 'test/styleTest/Loading'() {
        if(__DEV__ ! = =true) {
            return null;
        }
        return require('./test/styleTest/Loading').default;
    },
    get 'test/styleTest/Modal'() {
        if(! __DEV__) {return null;
        }
        return require('./test/styleTest/Modal').default;
    },
    get 'test/styleTest/TextAnimation'() {
        if (__DEV__) {
            return require('./test/styleTest/TextAnimation').default;
        } else {
            return null;
        }
    },
    get 'test/styleTest/TextTest'() {
        if(! __DEV__) {return null;
        } else {
            return require('./test/styleTest/TextTest').default; }},// This is an example of how to write something like this
    get 'test/styleTest/WithdrawResult'() { if (__DEV__) { return require('./test/styleTest/WithdrawResult').default; } return null; },
    get 'uplan/investType/InvestType'() { return require('./uplan/investType/InvestType').default; },
    get 'uplan/join/Join'() { return require('./uplan/join/Join').default; }};Copy the code

In fact, if you look at it carefully, there are two ways to write it, require inside or outside of if.

First look at the development mode of the bundle code, access to the local URL http://localhost:8081/index.bundle? platform=android&dev=true&minify=false :

    get 'test/story/StoryDisplay'() {
      if (__DEV__) {
        return_? _REQUIRE(_dependencyMap[58]."./test/story/StoryDisplay").default;
      }
      return null;
    },
      
    get 'test/styleTest/Loading'() {
      if(__DEV__ ! = =true) {
        return null;
      }
      return_? _REQUIRE(_dependencyMap[59]."./test/styleTest/Loading").default;
    },

    get 'test/styleTest/Modal'() {
      if(! __DEV__) {return null;
      }
      return_? _REQUIRE(_dependencyMap[60]."./test/styleTest/Modal").default;
    },

    get 'test/styleTest/TextAnimation'() {
      if (__DEV__) {
        return_? _REQUIRE(_dependencyMap[61]."./test/styleTest/TextAnimation").default;
      } else {
        return null;
      }
    },

    get 'test/styleTest/TextTest'() {
      if(! __DEV__) {return null;
      } else {
        return_? _REQUIRE(_dependencyMap[62]."./test/styleTest/TextTest").default; }},Copy the code

As you can see, in dev=true mode, all test pages are packaged by RN, and the output of the above source code is the same.

Look at the output production mode under the bundle, visit the URL http://localhost:8081/index.bundle? Platform = android&dev = false&minify = false:

    get 'test/story/StoryDisplay'() {
      return null;
    },

    get 'test/styleTest/Loading'() {{return null;
      }
      return_? _REQUIRE(_dependencyMap[57]).default;
    },

    get 'test/styleTest/Modal'() {{return null;
      }
      return_? _REQUIRE(_dependencyMap[58]).default;
    },

    get 'test/styleTest/TextAnimation'() {{return null;
      }
    },
      
    get 'test/styleTest/TextTest'() {{return null; }},Copy the code

Hmm, Interesting 🤔, it can be clearly seen that the final package result of some writing methods is not what we expected.

Obviously, the above test/styleTest/Loading and test/styleTest/Modal are problematic. If you search the bundle for the code for the two pages, you will find that the code for the two pages has been packaged into the bundle. _REQUIRE(_dependencyMap[58]).default

If you look closely at these two problematic lines of code, and compare them to the other ones, it’s easy to see that the problematic code, require, is not in the if/else branch.

conclusion

React Native allows you to selectively load JS files in different development or production environments by __DEV__. The branch of require, however, must be inside the corresponding if(__DEV__){}else{} to be filtered out when RN packages. RN package removes dead code in if/else; However, require outside of if/else, even if it is never executed, will be statically parsed, and the corresponding code will be typed into the final bundle.

React (Native) dead code removal: github.com/sophister/2…

AD time

Finally, welcome to star our renrendai big front-end team blog. All the articles will be updated to zhihu column and Nuggets account simultaneously. We will share several high quality big front-end technology articles every week. If you like this article, please give it a thumbs-up.