Files
react-native/docs/next/performance.html
T
Website Deployment Script b7c2cdaf52 Deploy website
Deploy website version based on 068c01bff8
2017-12-07 18:28:46 +00:00

638 lines
85 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<html><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1"/><title>Performance · React Native</title><meta name="viewport" content="width=device-width"/><meta name="generator" content="Docusaurus"/><meta property="og:title" content="Performance · React Native"/><meta property="og:type" content="website"/><meta property="og:url" content="https://facebook.github.io/react-native/index.html"/><meta property="og:description" content="A compelling reason for using React Native instead of WebView-based tools is to"/><link rel="shortcut icon" href="/react-native/img/favicon.png"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css"/><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/solarized-dark.min.css"/><link rel="alternate" type="application/atom+xml" href="https://facebook.github.io/blog/atom.xml" title="React Native Blog ATOM Feed"/><link rel="alternate" type="application/rss+xml" href="https://facebook.github.io/blog/feed.xml" title="React Native Blog RSS Feed"/><script type="text/javascript" src="https://snack.expo.io/embed.js"></script><script type="text/javascript" src="/react-native/js/codeblocks.js"></script><link rel="stylesheet" href="/react-native/css/main.css"/></head><body class="sideNavVisible"><div class="fixedHeaderContainer"><div class="headerWrapper wrapper"><header><a href="/react-native/"><img class="logo" src="/react-native/img/header_logo.png"/><h2 class="headerTitle">React Native</h2></a><a href="/react-native/versions.html"><h3>next</h3></a><div class="navigationWrapper navigationSlider"><nav class="slidingNav"><ul class="nav-site nav-site-internal"><li><a href="/react-native/docs/next/getting-started.html" target="_self">Docs</a></li><li><a href="/react-native/en/help.html" target="_self">Community</a></li><li><a href="/react-native/blog" target="_self">Blog</a></li><li class="navSearchWrapper reactNavSearchWrapper"><input type="text" id="search_input_react" placeholder="Search"/></li><li><a href="https://github.com/facebook/react-native" target="_self">GitHub</a></li><li><a href="https://reactjs.org/" target="_self">React</a></li></ul></nav></div></header></div></div><div class="navPusher"><div class="docMainWrapper wrapper"><div class="container docsNavContainer" id="docsNav"><nav class="toc"><div class="toggleNav"><section class="navWrapper wrapper"><div class="navBreadcrumb wrapper"><div class="navToggle" id="navToggler"><i></i></div><h2><i></i><span>Guides</span></h2></div><div class="navGroups"><div class="navGroup navGroupActive"><h3>The Basics</h3><ul><li class="navListItem"><a class="navItem" href="/react-native/docs/next/getting-started.html">Getting Started</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/tutorial.html">Learn the Basics</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/props.html">Props</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/state.html">State</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/style.html">Style</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/height-and-width.html">Height and Width</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/flexbox.html">Layout with Flexbox</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/handling-text-input.html">Handling Text Input</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/handling-touches.html">Handling Touches</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/using-a-scrollview.html">Using a ScrollView</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/using-a-listview.html">Using List Views</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/network.html">Networking</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/more-resources.html">More Resources</a></li></ul></div><div class="navGroup navGroupActive"><h3>Guides</h3><ul><li class="navListItem"><a class="navItem" href="/react-native/docs/next/components-and-apis.html">Components and APIs</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/platform-specific-code.html">Platform Specific Code</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/navigation.html">Navigating Between Screens</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/images.html">Images</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/animations.html">Animations</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/accessibility.html">Accessibility</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/improvingux.html">Improving User Experience</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/timers.html">Timers</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/debugging.html">Debugging</a></li><li class="navListItem navListItemActive"><a class="navItem navItemActive" href="/react-native/docs/next/performance.html">Performance</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/gesture-responder-system.html">Gesture Responder System</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/javascript-environment.html">JavaScript Environment</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/direct-manipulation.html">Direct Manipulation</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/colors.html">Color Reference</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/integration-with-existing-apps.html">Integration with Existing Apps</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/running-on-device.html">Running On Device</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/upgrading.html">Upgrading to new React Native versions</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/troubleshooting.html">Troubleshooting</a></li></ul></div><div class="navGroup navGroupActive"><h3>Guides (iOS)</h3><ul><li class="navListItem"><a class="navItem" href="/react-native/docs/next/native-modules-ios.html">Native Modules</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/native-components-ios.html">Native UI Components</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/linking-libraries-ios.html">Linking Libraries</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/running-on-simulator-ios.html">Running On Simulator</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/communication-ios.html">Communication between native and React Native</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/building-for-apple-tv.html">Building For Apple TV</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/app-extensions.html">App Extensions</a></li></ul></div><div class="navGroup navGroupActive"><h3>Guides (Android)</h3><ul><li class="navListItem"><a class="navItem" href="/react-native/docs/next/native-modules-android.html">Native Modules</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/native-components-android.html">Native UI Components</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/headless-js-android.html">Headless JS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/signed-apk-android.html">Generating Signed APK</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/android-building-from-source.html">Building React Native from source</a></li></ul></div><div class="navGroup navGroupActive"><h3>Contributing</h3><ul><li class="navListItem"><a class="navItem" href="/react-native/docs/next/contributing.html">How to Contribute</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/maintainers.html">What to Expect from Maintainers</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/testing.html">Testing your Changes</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/understanding-cli.html">Understanding the CLI</a></li></ul></div><div class="navGroup navGroupActive"><h3>Components</h3><ul><li class="navListItem"><a class="navItem" href="/react-native/docs/next/activityindicator.html">ActivityIndicator</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/button.html">Button</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/datepickerios.html">DatePickerIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/drawerlayoutandroid.html">DrawerLayoutAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/flatlist.html">FlatList</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/image.html">Image</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/keyboardavoidingview.html">KeyboardAvoidingView</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/listview.html">ListView</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/maskedviewios.html">MaskedViewIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/modal.html">Modal</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/navigatorios.html">NavigatorIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/picker.html">Picker</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/pickerios.html">PickerIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/progressbarandroid.html">ProgressBarAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/progressviewios.html">ProgressViewIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/refreshcontrol.html">RefreshControl</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/scrollview.html">ScrollView</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/sectionlist.html">SectionList</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/segmentedcontrolios.html">SegmentedControlIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/slider.html">Slider</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/snapshotviewios.html">SnapshotViewIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/statusbar.html">StatusBar</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/switch.html">Switch</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/tabbarios.html">TabBarIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/tabbarios-item.html">TabBarIOS.Item</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/text.html">Text</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/textinput.html">TextInput</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/toolbarandroid.html">ToolbarAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/touchablehighlight.html">TouchableHighlight</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/touchablenativefeedback.html">TouchableNativeFeedback</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/touchableopacity.html">TouchableOpacity</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/touchablewithoutfeedback.html">TouchableWithoutFeedback</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/view.html">View</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/viewpagerandroid.html">ViewPagerAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/virtualizedlist.html">VirtualizedList</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/webview.html">WebView</a></li></ul></div><div class="navGroup navGroupActive"><h3>APIs</h3><ul><li class="navListItem"><a class="navItem" href="/react-native/docs/next/accessibilityinfo.html">AccessibilityInfo</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/actionsheetios.html">ActionSheetIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/alert.html">Alert</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/alertios.html">AlertIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/animated.html">Animated</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/appregistry.html">AppRegistry</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/appstate.html">AppState</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/asyncstorage.html">AsyncStorage</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/backandroid.html">BackAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/backhandler.html">BackHandler</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/cameraroll.html">CameraRoll</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/clipboard.html">Clipboard</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/datepickerandroid.html">DatePickerAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/dimensions.html">Dimensions</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/easing.html">Easing</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/geolocation.html">Geolocation</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/imageeditor.html">ImageEditor</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/imagepickerios.html">ImagePickerIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/imagestore.html">ImageStore</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/image-style-props.html">Image Style Props</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/interactionmanager.html">InteractionManager</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/keyboard.html">Keyboard</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/layout-props.html">Layout Props</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/layoutanimation.html">LayoutAnimation</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/linking.html">Linking</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/listviewdatasource.html">ListViewDataSource</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/netinfo.html">NetInfo</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/panresponder.html">PanResponder</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/permissionsandroid.html">PermissionsAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/pixelratio.html">PixelRatio</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/pushnotificationios.html">PushNotificationIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/settings.html">Settings</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/shadow-props.html">Shadow Props</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/share.html">Share</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/statusbarios.html">StatusBarIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/stylesheet.html">StyleSheet</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/systrace.html">Systrace</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/text-style-props.html">Text Style Props</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/timepickerandroid.html">TimePickerAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/toastandroid.html">ToastAndroid</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/transforms.html">Transforms</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/vibration.html">Vibration</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/vibrationios.html">VibrationIOS</a></li><li class="navListItem"><a class="navItem" href="/react-native/docs/next/view-style-props.html">View Style Props</a></li></ul></div></div></section></div><script>
var toggler = document.getElementById('navToggler');
var nav = document.getElementById('docsNav');
toggler.onclick = function() {
nav.classList.toggle('docsSliderActive');
};
</script></nav></div><div class="container mainContainer"><div class="wrapper"><div class="post"><header class="postHeader"><a class="edit-page-link button" href="https://github.com/facebook/react-native-website/blob/master/docs/performance.md" target="_blank">Edit</a><h1>Performance</h1></header><article><div><span><p>A compelling reason for using React Native instead of WebView-based tools is to
achieve 60 frames per second and a native look and feel to your apps. Where
possible, we would like for React Native to do the right thing and help you to
focus on your app instead of performance optimization, but there are areas where
we're not quite there yet, and others where React Native (similar to writing
native code directly) cannot possibly determine the best way to optimize for you
and so manual intervention will be necessary. We try our best to deliver
buttery-smooth UI performance by default, but sometimes that just isn't
possible.</p>
<p>This guide is intended to teach you some basics to help you to
<a href="/react-native/docs/next/performance.html#profiling">troubleshoot performance issues</a>, as well as discuss
<a href="/react-native/docs/next/performance.html#common-sources-of-performance-problems">common sources of problems and their suggested solutions</a>.</p>
<h2><a class="anchor" aria-hidden="true" name="what-you-need-to-know-about-frames"></a><a href="#what-you-need-to-know-about-frames" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>What you need to know about frames</h2>
<p>Your grandparents' generation called movies
<a href="https://www.youtube.com/watch?v=F1i40rnpOsA">&quot;moving pictures&quot;</a> for a reason:
realistic motion in video is an illusion created by quickly changing static
images at a consistent speed. We refer to each of these images as frames. The
number of frames that is displayed each second has a direct impact on how smooth
and ultimately life-like a video (or user interface) seems to be. iOS devices
display 60 frames per second, which gives you and the UI system about 16.67ms to
do all of the work needed to generate the static image (frame) that the user
will see on the screen for that interval. If you are unable to do the work
necessary to generate that frame within the allotted 16.67ms, then you will
&quot;drop a frame&quot; and the UI will appear unresponsive.</p>
<p>Now to confuse the matter a little bit, open up the developer menu in your app
and toggle <code>Show Perf Monitor</code>. You will notice that there are two different
frame rates.</p>
<p><img src="/react-native/docs/assets/PerfUtil.png" alt=""></p>
<h3><a class="anchor" aria-hidden="true" name="js-frame-rate-javascript-thread"></a><a href="#js-frame-rate-javascript-thread" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JS frame rate (JavaScript thread)</h3>
<p>For most React Native applications, your business logic will run on the
JavaScript thread. This is where your React application lives, API calls are
made, touch events are processed, etc... Updates to native-backed views are
batched and sent over to the native side at the end of each iteration of the
event loop, before the frame deadline (if all goes well). If the JavaScript
thread is unresponsive for a frame, it will be considered a dropped frame. For
example, if you were to call <code>this.setState</code> on the root component of a complex
application and it resulted in re-rendering computationally expensive component
subtrees, it's conceivable that this might take 200ms and result in 12 frames
being dropped. Any animations controlled by JavaScript would appear to freeze
during that time. If anything takes longer than 100ms, the user will feel it.</p>
<p>This often happens during <code>Navigator</code> transitions: when you push a new route,
the JavaScript thread needs to render all of the components necessary for the
scene in order to send over the proper commands to the native side to create the
backing views. It's common for the work being done here to take a few frames and
cause <a href="http://jankfree.org/">jank</a> because the transition is controlled by the
JavaScript thread. Sometimes components will do additional work on
<code>componentDidMount</code>, which might result in a second stutter in the transition.</p>
<p>Another example is responding to touches: if you are doing work across multiple
frames on the JavaScript thread, you might notice a delay in responding to
<code>TouchableOpacity</code>, for example. This is because the JavaScript thread is busy
and cannot process the raw touch events sent over from the main thread. As a
result, <code>TouchableOpacity</code> cannot react to the touch events and command the
native view to adjust its opacity.</p>
<h3><a class="anchor" aria-hidden="true" name="ui-frame-rate-main-thread"></a><a href="#ui-frame-rate-main-thread" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>UI frame rate (main thread)</h3>
<p>Many people have noticed that performance of <code>NavigatorIOS</code> is better out of the
box than <code>Navigator</code>. The reason for this is that the animations for the
transitions are done entirely on the main thread, and so they are not
interrupted by frame drops on the JavaScript thread.</p>
<p>Similarly, you can happily scroll up and down through a <code>ScrollView</code> when the
JavaScript thread is locked up because the <code>ScrollView</code> lives on the main
thread. The scroll events are dispatched to the JS thread, but their receipt is
not necessary for the scroll to occur.</p>
<h2><a class="anchor" aria-hidden="true" name="common-sources-of-performance-problems"></a><a href="#common-sources-of-performance-problems" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Common sources of performance problems</h2>
<h3><a class="anchor" aria-hidden="true" name="running-in-development-mode-dev-true"></a><a href="#running-in-development-mode-dev-true" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Running in development mode (<code>dev=true</code>)</h3>
<p>JavaScript thread performance suffers greatly when running in dev mode. This is
unavoidable: a lot more work needs to be done at runtime to provide you with
good warnings and error messages, such as validating propTypes and various other
assertions. Always make sure to test performance in
<a href="/react-native/docs/next/running-on-device.html#building-your-app-for-production">release builds</a>.</p>
<h3><a class="anchor" aria-hidden="true" name="using-consolelog-statements"></a><a href="#using-consolelog-statements" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Using <code>console.log</code> statements</h3>
<p>When running a bundled app, these statements can cause a big bottleneck in the
JavaScript thread. This includes calls from debugging libraries such as
<a href="https://github.com/evgenyrodionov/redux-logger">redux-logger</a>, so make sure to
remove them before bundling. You can also use this
<a href="https://babeljs.io/docs/plugins/transform-remove-console/">babel plugin</a> that
removes all the <code>console.*</code> calls. You need to install it first with <code>npm i babel-plugin-transform-remove-console --save</code>, and then edit the <code>.babelrc</code> file
under your project directory like this:</p>
<pre><code class="hljs css json">{
<span class="hljs-attr">"env"</span>: {
<span class="hljs-attr">"production"</span>: {
<span class="hljs-attr">"plugins"</span>: [<span class="hljs-string">"transform-remove-console"</span>]
}
}
}
</code></pre>
<p>This will automatically remove all <code>console.*</code> calls in the release (production)
versions of your project.</p>
<h3><a class="anchor" aria-hidden="true" name="listview-initial-rendering-is-too-slow-or-scroll-performance-is-bad-for-large-lists"></a><a href="#listview-initial-rendering-is-too-slow-or-scroll-performance-is-bad-for-large-lists" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><code>ListView</code> initial rendering is too slow or scroll performance is bad for large lists</h3>
<p>Use the new <a href="/react-native/docs/next/flatlist.html"><code>FlatList</code></a> or <a href="/react-native/docs/next/sectionlist.html"><code>SectionList</code></a>
component instead. Besides simplifying the API, the new list components also
have significant performance enhancements, the main one being nearly constant
memory usage for any number of rows.</p>
<p>If your <a href="/react-native/docs/next/flatlist.html"><code>FlatList</code></a> is rendering slow, be sure that you've
implemented
<a href="https://facebook.github.io/react-native/flatlist.md#getitemlayout"><code>getItemLayout</code></a>
to optimize rendering speed by skipping measurement of the rendered items.</p>
<h3><a class="anchor" aria-hidden="true" name="js-fps-plunges-when-re-rendering-a-view-that-hardly-changes"></a><a href="#js-fps-plunges-when-re-rendering-a-view-that-hardly-changes" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JS FPS plunges when re-rendering a view that hardly changes</h3>
<p>If you are using a ListView, you must provide a <code>rowHasChanged</code> function that
can reduce a lot of work by quickly determining whether or not a row needs to be
re-rendered. If you are using immutable data structures, this would be as simple
as a reference equality check.</p>
<p>Similarly, you can implement <code>shouldComponentUpdate</code> and indicate the exact
conditions under which you would like the component to re-render. If you write
pure components (where the return value of the render function is entirely
dependent on props and state), you can leverage PureRenderMixin to do this for
you. Once again, immutable data structures are useful to keep this fast -- if
you have to do a deep comparison of a large list of objects, it may be that
re-rendering your entire component would be quicker, and it would certainly
require less code.</p>
<h3><a class="anchor" aria-hidden="true" name="dropping-js-thread-fps-because-of-doing-a-lot-of-work-on-the-javascript-thread-at-the-same-time"></a><a href="#dropping-js-thread-fps-because-of-doing-a-lot-of-work-on-the-javascript-thread-at-the-same-time" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Dropping JS thread FPS because of doing a lot of work on the JavaScript thread at the same time</h3>
<p>&quot;Slow Navigator transitions&quot; is the most common manifestation of this, but there
are other times this can happen. Using InteractionManager can be a good
approach, but if the user experience cost is too high to delay work during an
animation, then you might want to consider LayoutAnimation.</p>
<p>The Animated API currently calculates each keyframe on-demand on the JavaScript
thread unless you
<a href="https://facebook.github.io/react-native/blog/2017/02/14/using-native-driver-for-animated.html#how-do-i-use-this-in-my-app">set <code>useNativeDriver: true</code></a>,
while LayoutAnimation leverages Core Animation and is unaffected by JS thread
and main thread frame drops.</p>
<p>One case where I have used this is for animating in a modal (sliding down from
top and fading in a translucent overlay) while initializing and perhaps
receiving responses for several network requests, rendering the contents of the
modal, and updating the view where the modal was opened from. See the Animations
guide for more information about how to use LayoutAnimation.</p>
<p>Caveats:</p>
<ul>
<li>LayoutAnimation only works for fire-and-forget animations (&quot;static&quot;
animations) -- if it must be interruptible, you will need to use <code>Animated</code>.</li>
</ul>
<h3><a class="anchor" aria-hidden="true" name="moving-a-view-on-the-screen-scrolling-translating-rotating-drops-ui-thread-fps"></a><a href="#moving-a-view-on-the-screen-scrolling-translating-rotating-drops-ui-thread-fps" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Moving a view on the screen (scrolling, translating, rotating) drops UI thread FPS</h3>
<p>This is especially true when you have text with a transparent background
positioned on top of an image, or any other situation where alpha compositing
would be required to re-draw the view on each frame. You will find that enabling
<code>shouldRasterizeIOS</code> or <code>renderToHardwareTextureAndroid</code> can help with this
significantly.</p>
<p>Be careful not to overuse this or your memory usage could go through the roof.
Profile your performance and memory usage when using these props. If you don't
plan to move a view anymore, turn this property off.</p>
<h3><a class="anchor" aria-hidden="true" name="animating-the-size-of-an-image-drops-ui-thread-fps"></a><a href="#animating-the-size-of-an-image-drops-ui-thread-fps" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Animating the size of an image drops UI thread FPS</h3>
<p>On iOS, each time you adjust the width or height of an Image component it is
re-cropped and scaled from the original image. This can be very expensive,
especially for large images. Instead, use the <code>transform: [{scale}]</code> style
property to animate the size. An example of when you might do this is when you
tap an image and zoom it in to full screen.</p>
<h3><a class="anchor" aria-hidden="true" name="my-touchablex-view-isn-t-very-responsive"></a><a href="#my-touchablex-view-isn-t-very-responsive" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>My TouchableX view isn't very responsive</h3>
<p>Sometimes, if we do an action in the same frame that we are adjusting the
opacity or highlight of a component that is responding to a touch, we won't see
that effect until after the <code>onPress</code> function has returned. If <code>onPress</code> does a
<code>setState</code> that results in a lot of work and a few frames dropped, this may
occur. A solution to this is to wrap any action inside of your <code>onPress</code> handler
in <code>requestAnimationFrame</code>:</p>
<pre><code class="hljs css js">handleOnPress() {
<span class="hljs-comment">// Always use TimerMixin with requestAnimationFrame, setTimeout and</span>
<span class="hljs-comment">// setInterval</span>
<span class="hljs-keyword">this</span>.requestAnimationFrame(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
<span class="hljs-keyword">this</span>.doExpensiveAction();
});
}
</code></pre>
<h3><a class="anchor" aria-hidden="true" name="slow-navigator-transitions"></a><a href="#slow-navigator-transitions" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Slow navigator transitions</h3>
<p>As mentioned above, <code>Navigator</code> animations are controlled by the JavaScript
thread. Imagine the &quot;push from right&quot; scene transition: each frame, the new
scene is moved from the right to left, starting offscreen (let's say at an
x-offset of 320) and ultimately settling when the scene sits at an x-offset of</p>
<ol>
<li>Each frame during this transition, the JavaScript thread needs to send a new
x-offset to the main thread. If the JavaScript thread is locked up, it cannot do
this and so no update occurs on that frame and the animation stutters.</li>
</ol>
<p>One solution to this is to allow for JavaScript-based animations to be offloaded
to the main thread. If we were to do the same thing as in the above example with
this approach, we might calculate a list of all x-offsets for the new scene when
we are starting the transition and send them to the main thread to execute in an
optimized way. Now that the JavaScript thread is freed of this responsibility,
it's not a big deal if it drops a few frames while rendering the scene -- you
probably won't even notice because you will be too distracted by the pretty
transition.</p>
<p>Solving this is one of the main goals behind the new
<a href="/react-native/docs/next/navigation.html">React Navigation</a> library. The views in React Navigation use
native components and the <a href="/react-native/docs/next/animated.html"><code>Animated</code></a> library to deliver 60 FPS
animations that are run on the native thread.</p>
<h2><a class="anchor" aria-hidden="true" name="profiling"></a><a href="#profiling" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Profiling</h2>
<p>Use the built-in profiler to get detailed information about work done in the
JavaScript thread and main thread side-by-side. Access it by selecting Perf
Monitor from the Debug menu.</p>
<p>For iOS, Instruments is an invaluable tool, and on Android you should learn to
use <a href="/react-native/docs/next/performance.html#profiling-android-ui-performance-with-systrace"><code>systrace</code></a>.</p>
<p>Another way to profile JavaScript is to use the Chrome profiler while debugging.
This won't give you accurate results as the code is running in Chrome but will
give you a general idea of where bottlenecks might be.</p>
<p>But first,
<a href="/react-native/docs/next/performance.html#running-in-development-mode-dev-true"><strong>make sure that Development Mode is OFF!</strong></a>
You should see <code>__DEV__ === false, development-level warning are OFF, performance optimizations are ON</code> in your application logs.</p>
<h3><a class="anchor" aria-hidden="true" name="profiling-android-ui-performance-with-systrace"></a><a href="#profiling-android-ui-performance-with-systrace" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Profiling Android UI Performance with <code>systrace</code></h3>
<p>Android supports 10k+ different phones and is generalized to support software
rendering: the framework architecture and need to generalize across many
hardware targets unfortunately means you get less for free relative to iOS. But
sometimes, there are things you can improve -- and many times it's not native
code's fault at all!</p>
<p>The first step for debugging this jank is to answer the fundamental question of
where your time is being spent during each 16ms frame. For that, we'll be using
a standard Android profiling tool called <code>systrace</code>.</p>
<p><code>systrace</code> is a standard Android marker-based profiling tool (and is installed
when you install the Android platform-tools package). Profiled code blocks are
surrounded by start/end markers which are then visualized in a colorful chart
format. Both the Android SDK and React Native framework provide standard markers
that you can visualize.</p>
<h4><a class="anchor" aria-hidden="true" name="1-collecting-a-trace"></a><a href="#1-collecting-a-trace" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>1. Collecting a trace</h4>
<p>First, connect a device that exhibits the stuttering you want to investigate to
your computer via USB and get it to the point right before the
navigation/animation you want to profile. Run <code>systrace</code> as follows:</p>
<pre><code class="hljs">$ &lt;path_to_android_sdk&gt;/platform-tools/systrace/systrace<span class="hljs-selector-class">.py</span> --time=<span class="hljs-number">10</span> -o trace<span class="hljs-selector-class">.html</span> sched gfx view -<span class="hljs-selector-tag">a</span> &lt;your_package_name&gt;
</code></pre>
<p>A quick breakdown of this command:</p>
<ul>
<li><code>time</code> is the length of time the trace will be collected in seconds</li>
<li><code>sched</code>, <code>gfx</code>, and <code>view</code> are the android SDK tags (collections of markers)
we care about: <code>sched</code> gives you information about what's running on each core
of your phone, <code>gfx</code> gives you graphics info such as frame boundaries, and
<code>view</code> gives you information about measure, layout, and draw passes</li>
<li><code>-a &lt;your_package_name&gt;</code> enables app-specific markers, specifically the ones
built into the React Native framework. <code>your_package_name</code> can be found in the
<code>AndroidManifest.xml</code> of your app and looks like <code>com.example.app</code></li>
</ul>
<p>Once the trace starts collecting, perform the animation or interaction you care
about. At the end of the trace, systrace will give you a link to the trace which
you can open in your browser.</p>
<h4><a class="anchor" aria-hidden="true" name="2-reading-the-trace"></a><a href="#2-reading-the-trace" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>2. Reading the trace</h4>
<p>After opening the trace in your browser (preferably Chrome), you should see
something like this:</p>
<p><img src="/react-native/docs/assets/SystraceExample.png" alt="Example"></p>
<blockquote>
<p><strong>HINT</strong>: Use the WASD keys to strafe and zoom</p>
</blockquote>
<p>If your trace .html file isn't opening correctly, check your browser console for
the following:</p>
<p><img src="/react-native/docs/assets/ObjectObserveError.png" alt="ObjectObserveError"></p>
<p>Since <code>Object.observe</code> was deprecated in recent browsers, you may have to open
the file from the Google Chrome Tracing tool. You can do so by:</p>
<ul>
<li>Opening tab in chrome <a href="chrome://tracing">chrome://tracing</a></li>
<li>Selecting load</li>
<li>Selecting the html file generated from the previous command.</li>
</ul>
<blockquote>
<p><strong>Enable VSync highlighting</strong></p>
<p>Check this checkbox at the top right of the screen to highlight the 16ms frame
boundaries:</p>
<p><img src="/react-native/docs/assets/SystraceHighlightVSync.png" alt="Enable VSync Highlighting"></p>
<p>You should see zebra stripes as in the screenshot above. If you don't, try
profiling on a different device: Samsung has been known to have issues
displaying vsyncs while the Nexus series is generally pretty reliable.</p>
</blockquote>
<h4><a class="anchor" aria-hidden="true" name="3-find-your-process"></a><a href="#3-find-your-process" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>3. Find your process</h4>
<p>Scroll until you see (part of) the name of your package. In this case, I was
profiling <code>com.facebook.adsmanager</code>, which shows up as <code>book.adsmanager</code> because
of silly thread name limits in the kernel.</p>
<p>On the left side, you'll see a set of threads which correspond to the timeline
rows on the right. There are a few threads we care about for our purposes: the
UI thread (which has your package name or the name UI Thread), <code>mqt_js</code>, and
<code>mqt_native_modules</code>. If you're running on Android 5+, we also care about the
Render Thread.</p>
<ul>
<li><p><strong>UI Thread.</strong> This is where standard android measure/layout/draw happens. The
thread name on the right will be your package name (in my case
book.adsmanager) or UI Thread. The events that you see on this thread should
look something like this and have to do with <code>Choreographer</code>, <code>traversals</code>,
and <code>DispatchUI</code>:</p>
<p><img src="/react-native/docs/assets/SystraceUIThreadExample.png" alt="UI Thread Example"></p></li>
<li><p><strong>JS Thread.</strong> This is where JavaScript is executed. The thread name will be
either <code>mqt_js</code> or <code>&lt;...&gt;</code> depending on how cooperative the kernel on your
device is being. To identify it if it doesn't have a name, look for things
like <code>JSCall</code>, <code>Bridge.executeJSCall</code>, etc:</p>
<p><img src="/react-native/docs/assets/SystraceJSThreadExample.png" alt="JS Thread Example"></p></li>
<li><p><strong>Native Modules Thread.</strong> This is where native module calls (e.g. the
<code>UIManager</code>) are executed. The thread name will be either <code>mqt_native_modules</code>
or <code>&lt;...&gt;</code>. To identify it in the latter case, look for things like
<code>NativeCall</code>, <code>callJavaModuleMethod</code>, and <code>onBatchComplete</code>:</p>
<p><img src="/react-native/docs/assets/SystraceNativeModulesThreadExample.png" alt="Native Modules Thread Example"></p></li>
<li><p><strong>Bonus: Render Thread.</strong> If you're using Android L (5.0) and up, you will
also have a render thread in your application. This thread generates the
actual OpenGL commands used to draw your UI. The thread name will be either
<code>RenderThread</code> or <code>&lt;...&gt;</code>. To identify it in the latter case, look for things
like <code>DrawFrame</code> and <code>queueBuffer</code>:</p>
<p><img src="/react-native/docs/assets/SystraceRenderThreadExample.png" alt="Render Thread Example"></p></li>
</ul>
<h4><a class="anchor" aria-hidden="true" name="identifying-a-culprit"></a><a href="#identifying-a-culprit" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Identifying a culprit</h4>
<p>A smooth animation should look something like the following:</p>
<p><img src="/react-native/docs/assets/SystraceWellBehaved.png" alt="Smooth Animation"></p>
<p>Each change in color is a frame -- remember that in order to display a frame,
all our UI work needs to be done by the end of that 16ms period. Notice that no
thread is working close to the frame boundary. An application rendering like
this is rendering at 60 FPS.</p>
<p>If you noticed chop, however, you might see something like this:</p>
<p><img src="/react-native/docs/assets/SystraceBadJS.png" alt="Choppy Animation from JS"></p>
<p>Notice that the JS thread is executing basically all the time, and across frame
boundaries! This app is not rendering at 60 FPS. In this case, <strong>the problem
lies in JS</strong>.</p>
<p>You might also see something like this:</p>
<p><img src="/react-native/docs/assets/SystraceBadUI.png" alt="Choppy Animation from UI"></p>
<p>In this case, the UI and render threads are the ones that have work crossing
frame boundaries. The UI that we're trying to render on each frame is requiring
too much work to be done. In this case, <strong>the problem lies in the native views
being rendered</strong>.</p>
<p>At this point, you'll have some very helpful information to inform your next
steps.</p>
<h4><a class="anchor" aria-hidden="true" name="resolving-javascript-issues"></a><a href="#resolving-javascript-issues" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Resolving JavaScript issues</h4>
<p>If you identified a JS problem, look for clues in the specific JS that you're
executing. In the scenario above, we see <code>RCTEventEmitter</code> being called multiple
times per frame. Here's a zoom-in of the JS thread from the trace above:</p>
<p><img src="img/SystraceBadJS2.png" alt="Too much JS"></p>
<p>This doesn't seem right. Why is it being called so often? Are they actually
different events? The answers to these questions will probably depend on your
product code. And many times, you'll want to look into
<a href="https://facebook.github.io/react/component-specs.md#updating-shouldcomponentupdate">shouldComponentUpdate</a>.</p>
<h4><a class="anchor" aria-hidden="true" name="resolving-native-ui-issues"></a><a href="#resolving-native-ui-issues" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Resolving native UI Issues</h4>
<p>If you identified a native UI problem, there are usually two scenarios:</p>
<ol>
<li>the UI you're trying to draw each frame involves too much work on the GPU, or</li>
<li>You're constructing new UI during the animation/interaction (e.g. loading in
new content during a scroll).</li>
</ol>
<h5><a class="anchor" aria-hidden="true" name="too-much-gpu-work"></a><a href="#too-much-gpu-work" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Too much GPU work</h5>
<p>In the first scenario, you'll see a trace that has the UI thread and/or Render
Thread looking like this:</p>
<p><img src="/react-native/docs/assets/SystraceBadUI.png" alt="Overloaded GPU"></p>
<p>Notice the long amount of time spent in <code>DrawFrame</code> that crosses frame
boundaries. This is time spent waiting for the GPU to drain its command buffer
from the previous frame.</p>
<p>To mitigate this, you should:</p>
<ul>
<li>investigate using <code>renderToHardwareTextureAndroid</code> for complex, static content
that is being animated/transformed (e.g. the <code>Navigator</code> slide/alpha
animations)</li>
<li>make sure that you are <strong>not</strong> using <code>needsOffscreenAlphaCompositing</code>, which
is disabled by default, as it greatly increases the per-frame load on the GPU
in most cases.</li>
</ul>
<p>If these don't help and you want to dig deeper into what the GPU is actually
doing, you can check out
<a href="http://developer.android.com/tools/help/gltracer.html">Tracer for OpenGL ES</a>.</p>
<h5><a class="anchor" aria-hidden="true" name="creating-new-views-on-the-ui-thread"></a><a href="#creating-new-views-on-the-ui-thread" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Creating new views on the UI thread</h5>
<p>In the second scenario, you'll see something more like this:</p>
<p><img src="/react-native/docs/assets/SystraceBadCreateUI.png" alt="Creating Views"></p>
<p>Notice that first the JS thread thinks for a bit, then you see some work done on
the native modules thread, followed by an expensive traversal on the UI thread.</p>
<p>There isn't an easy way to mitigate this unless you're able to postpone creating
new UI until after the interaction, or you are able to simplify the UI you're
creating. The react native team is working on a infrastructure level solution
for this that will allow new UI to be created and configured off the main
thread, allowing the interaction to continue smoothly.</p>
<h2><a class="anchor" aria-hidden="true" name="unbundling-inline-requires"></a><a href="#unbundling-inline-requires" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Unbundling + inline requires</h2>
<p>If you have a large app you may want to consider unbundling and using inline
requires. This is useful for apps that have a large number of screens which may
not ever be opened during a typical usage of the app. Generally it is useful to
apps that have large amounts of code that are not needed for a while after
startup. For instance the app includes complicated profile screens or lesser
used features, but most sessions only involve visiting the main screen of the
app for updates. We can optimize the loading of the bundle by using the unbundle
feature of the packager and requiring those features and screens inline (when
they are actually used).</p>
<h3><a class="anchor" aria-hidden="true" name="loading-javascript"></a><a href="#loading-javascript" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Loading JavaScript</h3>
<p>Before react-native can execute JS code, that code must be loaded into memory
and parsed. With a standard bundle if you load a 50mb bundle, all 50mb must be
loaded and parsed before any of it can be executed. The optimization behind
unbundling is that you can load only the portion of the 50mb that you actually
need at startup, and progressively load more of the bundle as those sections are
needed.</p>
<h3><a class="anchor" aria-hidden="true" name="inline-requires"></a><a href="#inline-requires" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Inline Requires</h3>
<p>Inline requires delay the requiring of a module or file until that file is
actually needed. A basic example would look like this:</p>
<h4><a class="anchor" aria-hidden="true" name="veryexpensivejs"></a><a href="#veryexpensivejs" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>VeryExpensive.js</h4>
<pre><code class="hljs"><span class="hljs-keyword">import</span> <span class="hljs-type">React</span>, { <span class="hljs-type">Component</span> } from <span class="hljs-symbol">'reac</span>t';
<span class="hljs-keyword">import</span> { <span class="hljs-type">Text</span> } from <span class="hljs-symbol">'react</span>-native';
<span class="hljs-comment">// ... import some very expensive modules</span>
<span class="hljs-comment">// You may want to log at the file level to verify when this is happening</span>
console.log(<span class="hljs-symbol">'VeryExpensive</span> component loaded');
export <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VeryExpensive</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
<span class="hljs-comment">// lots and lots of code</span>
render() {
<span class="hljs-keyword">return</span> &lt;<span class="hljs-type">Text</span>&gt;<span class="hljs-type">Very</span> <span class="hljs-type">Expensive</span> <span class="hljs-type">Component</span>&lt;/<span class="hljs-type">Text</span>&gt;;
}
}
</code></pre>
<h4><a class="anchor" aria-hidden="true" name="optimizedjs"></a><a href="#optimizedjs" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Optimized.js</h4>
<pre><code class="hljs"><span class="hljs-keyword">import</span> React, { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { TouchableOpacity, View, Text } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-native'</span>;
<span class="hljs-keyword">let</span> VeryExpensive = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Optimized</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
state = { <span class="hljs-attr">needsExpensive</span>: <span class="hljs-literal">false</span> };
didPress = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
<span class="hljs-keyword">if</span> (VeryExpensive == <span class="hljs-literal">null</span>) {
VeryExpensive = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./VeryExpensive'</span>).default;
}
<span class="hljs-keyword">this</span>.setState(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> ({
<span class="hljs-attr">needsExpensive</span>: <span class="hljs-literal">true</span>,
}));
};
render() {
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginTop:</span> <span class="hljs-attr">20</span> }}&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">TouchableOpacity</span> <span class="hljs-attr">onPress</span>=<span class="hljs-string">{this.didPress}</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>Load<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">TouchableOpacity</span>&gt;</span>
{this.state.needsExpensive ? <span class="hljs-tag">&lt;<span class="hljs-name">VeryExpensive</span> /&gt;</span> : null}
<span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span></span>
);
}
}
</code></pre>
<p>Even without unbundling inline requires can lead to startup time improvements,
because the code within VeryExpensive.js will only execute once it is required
for the first time.</p>
<h3><a class="anchor" aria-hidden="true" name="enable-unbundling"></a><a href="#enable-unbundling" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Enable Unbundling</h3>
<p>On iOS unbundling will create a single indexed file that react native will load
one module at a time. On Android, by default it will create a set of files for
each module. You can force Android to create a single file, like iOS, but using
multiple files can be more performant and requires less memory.</p>
<p>Enable unbundling in Xcode by editing the build phase &quot;Bundle React Native code
and images&quot;. Before
<code>../node_modules/react-native/packager/react-native-xcode.sh</code> add <code>export BUNDLE_COMMAND=&quot;unbundle&quot;</code>:</p>
<pre><code class="hljs"><span class="hljs-builtin-name">export</span> <span class="hljs-attribute">BUNDLE_COMMAND</span>=<span class="hljs-string">"unbundle"</span>
<span class="hljs-builtin-name">export</span> <span class="hljs-attribute">NODE_BINARY</span>=node
<span class="hljs-built_in">..</span>/node_modules/react-native/packager/react-native-xcode.sh
</code></pre>
<p>On Android enable unbundling by editing your android/app/build.gradle file.
Before the line <code>apply from: &quot;../../node_modules/react-native/react.gradle&quot;</code> add
or amend the <code>project.ext.react</code> block:</p>
<pre><code class="hljs">project<span class="hljs-selector-class">.ext</span><span class="hljs-selector-class">.react</span> = [
bundleCommand: <span class="hljs-string">"unbundle"</span>,
]
</code></pre>
<p>Use the following lines on Android if you want to use a single indexed file:</p>
<pre><code class="hljs">project<span class="hljs-selector-class">.ext</span><span class="hljs-selector-class">.react</span> = [
bundleCommand: <span class="hljs-string">"unbundle"</span>,
extraPackagerArgs: [<span class="hljs-string">"--indexed-unbundle"</span>]
]
</code></pre>
<h3><a class="anchor" aria-hidden="true" name="configure-preloading-and-inline-requires"></a><a href="#configure-preloading-and-inline-requires" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Configure Preloading and Inline Requires</h3>
<p>Now that we have unbundled our code, there is overhead for calling require.
require now needs to send a message over the bridge when it encounters a module
it has not loaded yet. This will impact startup the most, because that is where
the largest number of require calls are likely to take place while the app loads
the initial module. Luckily we can configure a portion of the modules to be
preloaded. In order to do this, you will need to implement some form of inline
require.</p>
<h3><a class="anchor" aria-hidden="true" name="adding-a-packager-config-file"></a><a href="#adding-a-packager-config-file" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Adding a packager config file</h3>
<p>Create a folder in your project called packager, and create a single file named
config.js. Add the following:</p>
<pre><code class="hljs">const<span class="hljs-built_in"> config </span>= {
getTransformOptions: () =&gt; {
return {
transform: { inlineRequires: <span class="hljs-literal">true</span> },
};
},
};
module.exports =<span class="hljs-built_in"> config;
</span></code></pre>
<p>In Xcode, in the build phase, include <code>export BUNDLE_CONFIG=&quot;packager/config.js&quot;</code>.</p>
<pre><code class="hljs"><span class="hljs-builtin-name">export</span> <span class="hljs-attribute">BUNDLE_COMMAND</span>=<span class="hljs-string">"unbundle"</span>
<span class="hljs-builtin-name">export</span> <span class="hljs-attribute">BUNDLE_CONFIG</span>=<span class="hljs-string">"packager/config.js"</span>
<span class="hljs-builtin-name">export</span> <span class="hljs-attribute">NODE_BINARY</span>=node
<span class="hljs-built_in">..</span>/node_modules/react-native/packager/react-native-xcode.sh
</code></pre>
<p>Edit your android/app/build.gradle file to include <code>bundleConfig: &quot;packager/config.js&quot;,</code>.</p>
<pre><code class="hljs">project<span class="hljs-selector-class">.ext</span><span class="hljs-selector-class">.react</span> = [
bundleCommand: <span class="hljs-string">"unbundle"</span>,
bundleConfig: <span class="hljs-string">"packager/config.js"</span>
]
</code></pre>
<p>Finally, you can update &quot;start&quot; under &quot;scripts&quot; on your package.json to use the
config:</p>
<p><code>&quot;start&quot;: &quot;node node_modules/react-native/local-cli/cli.js start --config ../../../../packager/config.js&quot;,</code></p>
<p>Start your package server with <code>npm start</code>. Note that when the dev packager is
automatically launched via xcode and <code>react-native run-android</code>, etc, it does
not use <code>npm start</code>, so it won't use the config.</p>
<h3><a class="anchor" aria-hidden="true" name="investigating-the-loaded-modules"></a><a href="#investigating-the-loaded-modules" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Investigating the Loaded Modules</h3>
<p>In your root file (index.(ios|android).js) you can add the following after the
initial imports:</p>
<pre><code class="hljs"><span class="hljs-keyword">const</span> modules = <span class="hljs-built_in">require</span>.getModules();
<span class="hljs-keyword">const</span> moduleIds = <span class="hljs-built_in">Object</span>.keys(modules);
<span class="hljs-keyword">const</span> loadedModuleNames = moduleIds
.filter(<span class="hljs-function"><span class="hljs-params">moduleId</span> =&gt;</span> modules[moduleId].isInitialized)
.map(<span class="hljs-function"><span class="hljs-params">moduleId</span> =&gt;</span> modules[moduleId].verboseName);
<span class="hljs-keyword">const</span> waitingModuleNames = moduleIds
.filter(<span class="hljs-function"><span class="hljs-params">moduleId</span> =&gt;</span> !modules[moduleId].isInitialized)
.map(<span class="hljs-function"><span class="hljs-params">moduleId</span> =&gt;</span> modules[moduleId].verboseName);
<span class="hljs-comment">// make sure that the modules you expect to be waiting are actually waiting</span>
<span class="hljs-built_in">console</span>.log(
<span class="hljs-string">'loaded:'</span>,
loadedModuleNames.length,
<span class="hljs-string">'waiting:'</span>,
waitingModuleNames.length
);
<span class="hljs-comment">// grab this text blob, and put it in a file named packager/moduleNames.js</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`module.exports = <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(loadedModuleNames.sort())}</span>;`</span>);
</code></pre>
<p>When you run your app, you can look in the console and see how many modules have
been loaded, and how many are waiting. You may want to read the moduleNames and
see if there are any surprises. Note that inline requires are invoked the first
time the imports are referenced. You may need to investigate and refactor to
ensure only the modules you want are loaded on startup. Note that you can change
the Systrace object on require to help debug problematic requires.</p>
<pre><code class="hljs"><span class="hljs-built_in">require</span>.Systrace.beginEvent = <span class="hljs-function"><span class="hljs-params">(message)</span> =&gt;</span> {
<span class="hljs-keyword">if</span>(message.includes(problematicModule)) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error();
}
}
</code></pre>
<p>Every app is different, but it may make sense to only load the modules you need
for the very first screen. When you are satisified, put the output of the
loadedModuleNames into a file named packager/moduleNames.js.</p>
<h3><a class="anchor" aria-hidden="true" name="transforming-to-module-paths"></a><a href="#transforming-to-module-paths" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Transforming to Module Paths</h3>
<p>The loaded module names get us part of the way there, but we actually need
absolute module paths, so the next script will set that up. Add
<code>packager/generateModulePaths.js</code> to your project with the following:</p>
<pre><code class="hljs"><span class="hljs-comment">// @flow</span>
<span class="hljs-comment">/* eslint-disable no-console */</span>
<span class="hljs-keyword">const</span> execSync = <span class="hljs-built_in">require</span>(<span class="hljs-string">'child_process'</span>).execSync;
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> moduleNames = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./moduleNames'</span>);
<span class="hljs-keyword">const</span> pjson = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../package.json'</span>);
<span class="hljs-keyword">const</span> localPrefix = <span class="hljs-string">`<span class="hljs-subst">${pjson.name}</span>/`</span>;
<span class="hljs-keyword">const</span> modulePaths = moduleNames.map(<span class="hljs-function"><span class="hljs-params">moduleName</span> =&gt;</span> {
<span class="hljs-keyword">if</span> (moduleName.startsWith(localPrefix)) {
<span class="hljs-keyword">return</span> <span class="hljs-string">`./<span class="hljs-subst">${moduleName.substring(localPrefix.length)}</span>`</span>;
}
<span class="hljs-keyword">if</span> (moduleName.endsWith(<span class="hljs-string">'.js'</span>)) {
<span class="hljs-keyword">return</span> <span class="hljs-string">`./node_modules/<span class="hljs-subst">${moduleName}</span>`</span>;
}
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> result = execSync(
<span class="hljs-string">`grep "@providesModule <span class="hljs-subst">${moduleName}</span>" $(find . -name <span class="hljs-subst">${moduleName}</span>\\\\.js) -l`</span>
)
.toString()
.trim()
.split(<span class="hljs-string">'\n'</span>)[<span class="hljs-number">0</span>];
<span class="hljs-keyword">if</span> (result != <span class="hljs-literal">null</span>) {
<span class="hljs-keyword">return</span> result;
}
} <span class="hljs-keyword">catch</span> (e) {
<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
});
<span class="hljs-keyword">const</span> paths = modulePaths
.filter(<span class="hljs-function"><span class="hljs-params">path</span> =&gt;</span> path != <span class="hljs-literal">null</span>)
.map(<span class="hljs-function"><span class="hljs-params">path</span> =&gt;</span> <span class="hljs-string">`'<span class="hljs-subst">${path}</span>'`</span>)
.join(<span class="hljs-string">',\n'</span>);
<span class="hljs-keyword">const</span> fileData = <span class="hljs-string">`module.exports = [<span class="hljs-subst">${paths}</span>];`</span>;
fs.writeFile(<span class="hljs-string">'./packager/modulePaths.js'</span>, fileData, <span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
<span class="hljs-keyword">if</span> (err) {
<span class="hljs-built_in">console</span>.log(err);
}
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Done'</span>);
});
</code></pre>
<p>You can run via <code>node packager/modulePaths.js</code>.</p>
<p>This script attempts to map from the module names to module paths. Its not
foolproof though, for instance, it ignores platform specific files (*ios.js,
and *.android.js). However based on initial testing, it handles 95% of cases.
When it runs, after some time it should complete and output a file named
<code>packager/modulePaths.js</code>. It should contain paths to module files that are
relative to your projects root. You can commit modulePaths.js to your repo so it
is transportable.</p>
<h3><a class="anchor" aria-hidden="true" name="updating-the-configjs"></a><a href="#updating-the-configjs" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Updating the config.js</h3>
<p>Returning to packager/config.js we should update it to use our newly generated
modulePaths.js file.</p>
<pre><code class="hljs"><span class="hljs-keyword">const</span> modulePaths = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./modulePaths'</span>);
<span class="hljs-keyword">const</span> resolve = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>).resolve;
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> config = {
<span class="hljs-attr">getTransformOptions</span>: <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
<span class="hljs-keyword">const</span> moduleMap = {};
modulePaths.forEach(<span class="hljs-function"><span class="hljs-params">path</span> =&gt;</span> {
<span class="hljs-keyword">if</span> (fs.existsSync(path)) {
moduleMap[resolve(path)] = <span class="hljs-literal">true</span>;
}
});
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">preloadedModules</span>: moduleMap,
<span class="hljs-attr">transform</span>: { <span class="hljs-attr">inlineRequires</span>: { <span class="hljs-attr">blacklist</span>: moduleMap } },
};
},
};
<span class="hljs-built_in">module</span>.exports = config;
</code></pre>
<p>The preloadedModules entry in the config indicates which modules should be
marked as preloaded by the unbundler. When the bundle is loaded, those modules
are immediately loaded, before any requires have even executed. The blacklist
entry indicates that those modules should not be required inline. Because they
are preloaded, there is no performance benefit from using an inline require. In
fact the javascript spends extra time resolving the inline require every time
the imports are referenced.</p>
<h3><a class="anchor" aria-hidden="true" name="test-and-measure-improvements"></a><a href="#test-and-measure-improvements" aria-hidden="true" class="hash-link" ><svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Test and Measure Improvements</h3>
<p>You should now be ready to build your app using unbundling and inline requires.
Make sure you measure the before and after startup times.</p>
</span></div></article></div><div class="docs-prevnext"><a class="docs-prev button" href="debugging.html">← Debugging</a><a class="docs-next button" href="gesture-responder-system.html">Gesture Responder System →</a></div></div></div></div><footer class="nav-footer" id="footer"><section class="sitemap"><a href="/react-native/" class="nav-home"><img src="/react-native/img/header_logo.png" alt="React Native" width="66" height="58"/></a><div><h5><a href="/react-native/docs/getting-started.html">Docs</a></h5><a href="/react-native/docs/getting-started.html">Getting Started</a><a href="/react-native/docs/tutorial.html">Learn the Basics</a><a href="/react-native/docs/components-and-apis.html">Components and APIs</a><a href="/react-native/docs/more-resources.html">More Resources</a></div><div><h5><a href="/react-native/help.html">Community</a></h5><a href="/react-native/showcase.html">Who&#x27;s using React Native?</a><a href="http://www.meetup.com/topics/react-native/" target="_blank">Meetups</a><a href="https://www.facebook.com/groups/react.native.community" target="_blank">Facebook Group</a><a href="https://twitter.com/reactnative" target="_blank">Twitter</a></div><div><h5><a href="/react-native/help.html">Help</a></h5><a href="http://stackoverflow.com/questions/tagged/react-native" target="_blank">Stack Overflow</a><a href="https://discord.gg/0ZcbPKXt5bZjGY5n">Reactiflux Chat</a><a href="/react-native/versions.html" target="_blank">Latest Releases</a><a href="https://react-native.canny.io/feature-requests" target="_blank">Feature Requests</a></div><div><h5>More</h5><a href="/react-native/blog">Blog</a><a href="http://reactjs.org" target="_blank">React</a><a href="https://github.com/facebook/react-native" target="_blank">GitHub</a><div class="githubButton"><a class="github-button" href="https://github.com/facebook/react-native" data-icon="octicon-star" data-show-count="true" data-count-href="/facebook/react-native/stargazers" data-count-api="/repos/facebook/react-native#stargazers_count" data-count-aria-label="# stargazers on GitHub" aria-label="Star facebook/react-native on GitHub">Star</a></div></div></section><a href="https://code.facebook.com/projects/" target="_blank" class="fbOpenSource"><img src="/react-native/img/oss_logo.png" alt="Facebook Open Source" width="170" height="45"/></a><section class="copyright">Copyright © 2017 Facebook Inc.</section></footer></div><script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script><script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-41298772-2', 'auto');
ga('send', 'pageview');
</script><script>window.fbAsyncInit = function() {FB.init({appId:'1677033832619985',xfbml:true,version:'v2.7'});};(function(d, s, id){var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) {return;}js = d.createElement(s); js.id = id;js.src = '//connect.facebook.net/en_US/sdk.js';fjs.parentNode.insertBefore(js, fjs);}(document, 'script','facebook-jssdk'));
</script><script>window.twttr=(function(d,s, id){var js,fjs=d.getElementsByTagName(s)[0],t=window.twttr||{};if(d.getElementById(id))return t;js=d.createElement(s);js.id=id;js.src='https://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js, fjs);t._e = [];t.ready = function(f) {t._e.push(f);};return t;}(document, 'script', 'twitter-wjs'));</script><script>
var search = docsearch({
apiKey: '2c98749b4a1e588efec53b2acec13025',
indexName: 'react-native-versions',
inputSelector: '#search_input_react',
algoliaOptions: {"facetFilters":["tags:next"],"hitsPerPage":5}
});
</script></body></html>