Mobile Development 18 min read

Introducing and Optimizing React Native at Ctrip: Challenges, Solutions, and Future Plans

This article details Ctrip's adoption of React Native, explaining why it was introduced, the framework and tooling built around it, the performance and stability problems encountered, the various bundle‑splitting and engine‑caching solutions applied, and the roadmap for further cross‑platform enhancements.

Ctrip Technology
Ctrip Technology
Ctrip Technology
Introducing and Optimizing React Native at Ctrip: Challenges, Solutions, and Future Plans

This article, presented by Zhao Xinguo, senior technical manager of Ctrip's wireless development team, shares the experience of introducing React Native (RN) into Ctrip's travel app and the subsequent optimization work.

Background and usage : RN has been open‑source for over a year and is used by many Chinese internet companies. Ctrip began investing in RN in May and now promotes it to multiple business teams. The main motivations are reducing app size (RN bundles are far smaller than native code), improving user experience (RN renders native components via JavaScriptCore), leveraging a relatively mature and stable cross‑platform framework, supporting dynamic updates (especially on iOS where native code cannot be hot‑loaded), and enabling a single codebase for iOS and Android.

How RN was introduced : Based on RN 0.30, Ctrip built the CRN framework to simplify development. The framework provides: CLI tools for creating and running CRN projects. Packaging tools for building bundles. Extended components that wrap official RN APIs and add Ctrip‑specific APIs. Stability and performance optimizations such as faster page loading and crash reduction. A unified publishing workflow and documentation support.

Business usage : Four development cycles, each about one month, show rapid adoption and a sharp increase in RN pages and business modules.

Problems and optimizations : Four common issues were identified: (1) oversized JS bundles, (2) slow first‑page load, (3) RN‑induced crashes, and (4) ListView lag with large data sets. Detailed analysis revealed that the RN framework itself occupies ~530 KB of a 531 KB bundle, which becomes prohibitive when many business bundles are combined. Three splitting schemes were explored:

1. Common + Business split : Create an empty project that only requires react and react‑native , bundle it as common.js , then bundle each business module separately and remove the common part from the business bundle. The two files are merged at runtime. // Steps (simplified) 1. Create empty entry file with only "require('react'); require('react-native');" 2. Run react-native bundle → common.js 3. Bundle business code → business_all.js 4. Strip common code from business_all.js → business.js 5. Load common.js + business.js together at app start

2. FakeApp pre‑load : A lightweight fake RN app pre‑loads the common engine in the background, then receives a message to render the actual business module when needed. This reduces the perceived load time but still suffers when a single business bundle exceeds 500 KB.

3. Unbundle (module‑level splitting) : Inspired by Facebook's unbundle approach, each JS module is emitted as an individual file (e.g., 12.js , 13.js ) with a manifest that maps module IDs to files. The engine loads modules on demand, dramatically cutting initial execution time. This works on Android; iOS uses a pre‑pack binary format due to file‑IO constraints.

Engine lifecycle and caching : The JS engine states are defined as Loading → Ready → Dirty → Recycle. Common.js is cached; when a Ready engine is used it becomes Dirty, and after a threshold (e.g., two dirty engines) the engine recycles by clearing module caches.

Error handling : iOS crashes mainly stem from RCTFatalException . Setting a custom fatal handler resolves them: void RCTSetFatalHandler(RCTFatalHandler fatalHandler); Android crashes include RuntimeException during bundle loading, NativeExceptionsManager errors, and UnsatisfiedLinkError for native libraries. Adding try‑catch around System.loadLibrary and handling exceptions in ReactInstanceManagerImp mitigates these issues.

ListView performance : The default RN ListView renders off‑screen items, causing lag. Ctrip developed CRNListView with reusable cells—using ReactNativeTableView on iOS and RecyclerView on Android—plus pull‑to‑refresh, load‑more, and index bar features. Benchmarks show comparable performance on small data sets and clear advantages on large ones.

Future roadmap : Plans include CRN‑Web (converting RN code to run on H5 via a webpack‑like tool), a single JS engine capable of hosting multiple RN business modules, and exploring AMD‑style on‑demand module execution to further reduce bundle load time.

The article concludes with an invitation to join Ctrip's technical communities, download the PPT, and contact the tech assistant for further RN discussions.

Mobile DevelopmentPerformance OptimizationCrash HandlingReact NativeBundle Splitting
Ctrip Technology
Written by

Ctrip Technology

Official Ctrip Technology account, sharing and discussing growth.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.