mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
b7c2cdaf52
Deploy website version based on 068c01bff8
638 lines
85 KiB
HTML
638 lines
85 KiB
HTML
<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">"moving pictures"</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
|
||
"drop a frame" 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>"Slow Navigator transitions" 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 ("static"
|
||
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> =></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 "push from right" 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">$ <path_to_android_sdk>/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> <your_package_name>
|
||
</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 <your_package_name></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><...></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><...></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><...></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> <<span class="hljs-type">Text</span>><span class="hljs-type">Very</span> <span class="hljs-type">Expensive</span> <span class="hljs-type">Component</span></<span class="hljs-type">Text</span>>;
|
||
}
|
||
}
|
||
</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> =></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> =></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"><<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> }}></span>
|
||
<span class="hljs-tag"><<span class="hljs-name">TouchableOpacity</span> <span class="hljs-attr">onPress</span>=<span class="hljs-string">{this.didPress}</span>></span>
|
||
<span class="hljs-tag"><<span class="hljs-name">Text</span>></span>Load<span class="hljs-tag"></<span class="hljs-name">Text</span>></span>
|
||
<span class="hljs-tag"></<span class="hljs-name">TouchableOpacity</span>></span>
|
||
{this.state.needsExpensive ? <span class="hljs-tag"><<span class="hljs-name">VeryExpensive</span> /></span> : null}
|
||
<span class="hljs-tag"></<span class="hljs-name">View</span>></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 "Bundle React Native code
|
||
and images". Before
|
||
<code>../node_modules/react-native/packager/react-native-xcode.sh</code> add <code>export BUNDLE_COMMAND="unbundle"</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: "../../node_modules/react-native/react.gradle"</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: () => {
|
||
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="packager/config.js"</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: "packager/config.js",</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 "start" under "scripts" on your package.json to use the
|
||
config:</p>
|
||
<p><code>"start": "node node_modules/react-native/local-cli/cli.js start --config ../../../../packager/config.js",</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> =></span> modules[moduleId].isInitialized)
|
||
.map(<span class="hljs-function"><span class="hljs-params">moduleId</span> =></span> modules[moduleId].verboseName);
|
||
<span class="hljs-keyword">const</span> waitingModuleNames = moduleIds
|
||
.filter(<span class="hljs-function"><span class="hljs-params">moduleId</span> =></span> !modules[moduleId].isInitialized)
|
||
.map(<span class="hljs-function"><span class="hljs-params">moduleId</span> =></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> =></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> =></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> =></span> path != <span class="hljs-literal">null</span>)
|
||
.map(<span class="hljs-function"><span class="hljs-params">path</span> =></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> =></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> =></span> {
|
||
<span class="hljs-keyword">const</span> moduleMap = {};
|
||
modulePaths.forEach(<span class="hljs-function"><span class="hljs-params">path</span> =></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'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> |