How Recce Boosts Dynamic Front‑End Containers to Near‑Native Performance
This article analyzes Meituan's Recce solution for dynamic front‑end containers, detailing performance bottlenecks, architectural classifications, interpreter and language choices, UI framework design, rendering layer decisions, optimization techniques, and future directions to achieve native‑level speed while retaining dynamic capabilities.
1. The Performance Ceiling of Dynamic Containers
Since React Native’s launch in 2015, dynamic containers (often called "dynamic containers" in the text) have become a common front‑end strategy for platforms such as WeChat Mini‑Programs, Douyin Lynx, Pinduoduo Lego, Alipay Nebula/BirdNest, JD Taro‑Native, and Meituan’s own MRN and MMP/MSC. While they reduce development cost and improve deployment speed, they introduce extra layers—interpreters, communication, and serialization—that increase page load time and cause white‑screen issues.
Benchmarks on a simple page with 3,000 identical view nodes show that a dynamic container built with React Native can be up to five times slower than a native page, mainly because the logical interpreter adds significant computation and communication overhead.
2. Container Classification and Preliminary Thoughts
Containers can be grouped into three rendering approaches:
Web‑based: uses WebView and JavaScript/CSS for UI rendering.
Self‑drawn: leverages low‑level graphics APIs such as OpenGL and defines its own DSL.
System UI framework: builds on the platform’s native UI layer with an abstraction layer to smooth differences.
Further decomposition yields four layers: UI framework (business code), runtime support (interpreter or standard library), platform abstraction, and rendering layer.
3. Recce Selection and Construction
3.1 Interpreter & Programming Language Choice
The team decided on a Wasm‑first strategy, using Wasm3 as the interpreter because it is the fastest non‑JIT Wasm engine and has a tiny binary size. JavaScript (QuickJS) is kept as a secondary runtime for compatibility.
For the programming language, Rust was chosen over C, C++, and Go due to its performance, memory safety, and growing front‑end ecosystem. The final runtime stack consists of Rust compiled to Wasm running on Wasm3, with QuickJS as an auxiliary interpreter.
3.2 UI Framework
Because Rust lacks a mature UI ecosystem, the team built a custom UI framework inspired by Dioxus’s DSL and the Dominator observer pattern. The framework provides a declarative DSL, component encapsulation, state management, and aims for zero‑overhead abstractions without a virtual DOM, similar to SolidJS.
3.3 Rendering Layer
The rendering layer options were evaluated:
Reuse WebView – discarded due to poor performance.
Build a custom renderer – deemed infeasible because of the massive codebase required.
Leverage the system UI framework – chosen, essentially reusing React Native’s native rendering path, which already provides efficient component wrappers and layout via Yoga.
3.4 Overall Architecture
Recce’s architecture consists of a Host platform abstraction layer that optimizes property setting and abstracts platform differences. The layer follows a WebIDL‑like design, enabling any renderer or interpreter that complies with the standard interface to plug in easily. This design also simplifies future swaps of interpreters, languages, or UI frameworks.
4. Detailed Issues in Recce
The team identified property conversion as a major performance bottleneck in React Native. By replacing the dictionary‑based property transfer with an index‑based array, they reduced serialization overhead. They also generated code for property definitions across platforms (JS, Rust, Android, iOS, HarmonyOS) to keep the conversion path minimal.
Cross‑language calls were categorized into four groups (C↔C, Rust↔C, Java/JS↔C, and IPC‑style string interfaces). For stable interfaces they adopted a hybrid approach of hand‑written glue code plus code‑generated bindings (e.g., using wit‑bindgen for Rust‑Wasm3 and Mako templates for interface definitions).
5. Summary and Outlook
Recce achieves near‑native performance for dynamic containers by eliminating the virtual DOM, using a high‑efficiency Wasm interpreter, and optimizing property transfer. Benchmarks show up to 8× faster business‑logic execution and overall page‑load improvements of 2‑3× on real‑world workloads.
Future work includes improving developer experience (e.g., LLM‑assisted Rust onboarding), further performance gains via pure‑Rust execution, and possibly building a custom rendering engine to create a lightweight WebEngine‑like solution.
6. Q&A
Q: How does Recce’s performance compare to native solutions? A: On low‑end POS devices Recce’s UI matches Flutter’s performance while still running in an interpreted environment, effectively delivering native‑level speed.
Q: Will JavaScript become obsolete? A: No. Recce supports both a Rust‑based Wasm path (Recce‑rs) for performance‑critical scenarios and a JavaScript path (Recce‑js) for general use, allowing teams to choose based on cost‑benefit analysis.
Q: Why is the project named “Recce”? A: It references a rifle used by the U.S. Navy SEALs, symbolizing the high‑skill, high‑effort nature of achieving native‑grade performance while maintaining dynamic capabilities.
Q: Why pursue native‑level performance for a dynamic container? A: Better performance improves user experience, reduces white‑screen rates, and aligns with Meituan’s low‑margin, high‑scale business model that demands both rapid deployment and high‑quality UI across diverse, low‑end devices.
Meituan Technology Team
Over 10,000 engineers powering China’s leading lifestyle services e‑commerce platform. Supporting hundreds of millions of consumers, millions of merchants across 2,000+ industries. This is the public channel for the tech teams behind Meituan, Dianping, Meituan Waimai, Meituan Select, and related services.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
