Evolution of Hybrid Routing Architecture: From Hy 1.0 to Hy 2.0 with React, Webpack Code Splitting, and Performance Optimizations
This article traces the development of Qunar's internal hybrid routing solution from the SPA‑based Hy 1.0 to the React‑driven Hy 2.0, detailing the shift to webpack code‑splitting, async loading with react‑router, and the performance and packaging challenges solved along the way.
In 2014 I joined Qunar and now work in the mobile architecture group (YMFE) as a frontend engineer, focusing on new technology exploration, article translation, and blogging. My team is YMFE (http://ymfe.tech/).
After nearly two years of development, our internal hybrid solution hy has progressed from the mid‑2015 Hy 1.0 version to the React‑based Hy 2.0 released at the end of last year. With the framework upgrade, the routing architecture also changed significantly; this article explains the evolution of our routing choices and the techniques used to optimise page‑load performance.
Hy 1.0 Architecture
Hy 1.0 was built as a single‑page‑application (SPA) routing system, loading all static resources when the home page was entered. At the time, many Android browsers performed poorly and lacked page caching, so a SPA gave a native‑like experience and, combined with an offline‑package mechanism, mitigated long initial load times.
However, as Android hardware improved, browsers began caching pages and iOS introduced gesture‑back conflicts with SPA animations. Moreover, our hybrid environment used multiple WebViews, meaning each new WebView displayed only one logical page while still loading the entire bundle, which was inefficient.
We considered reverting to a multi‑page approach because it reduced frontend workload and bundle size, but this would sacrifice flexibility, especially for scenarios that require true SPA behaviour (e.g., offline file‑protocol apps) and would re‑introduce a dependency on backend routing.
The key question became: is there a solution that allows seamless switching between SPA and multi‑page modes while supporting on‑demand resource loading?
Hy 2.0 Architecture
Hy 2.0 adopts webpack code‑splitting for asynchronous resource loading and react‑router for on‑demand component loading, achieving both performance and flexibility.
Asynchronous Loading
The principle is to split JavaScript into a core bundle A.js required for the initial screen and secondary bundles B.js for other pages. The entry page loads A.js and inserts an async script tag to fetch B.js when needed.
Because JavaScript modules often have complex inter‑dependencies, the split files are loaded via a JSONP‑like mechanism that registers a global callback on window, allowing the secondary chunk to resolve its dependencies and inject its modules into the global module registry.
Webpack provides a simple API for this purpose: the require.ensure keyword. The following code demonstrates the original pattern from the article:
require('A.js');
require.ensure([], function () {
require('B.js');
});On‑Demand Loading
React‑router’s match method already supports asynchronous loading. By replacing a route’s component property with getComponent, we can load a component only when its route is matched.
Typical synchronous route configuration:
<Route path="/">
<IndexRoute component={HomePage} />
<Route path="list" component={List} />
<Route path="detail" component={Detail} />
</Route>Converted to an asynchronous version using require.ensure:
<Route path="list" getComponent={(_, cb) => {
require.ensure([], () => {
cb(null, require('./List'));
});
}} />Because the native async pattern is verbose, our Hy framework introduces a Babel plugin that adds a sugar syntax require.async:
const List = require.async('./list');
<Route path="list" getComponent={List} />Problems Encountered
Split Granularity : Not every resource should be split. We use the heuristic “size > bandwidth × TTFB” to decide. For example, on a 4G network with 800 KB/s bandwidth and a 0.02 s TTFB, resources smaller than 16 KB are left unsplit because the overhead of an extra request outweighs the download cost.
Offline Package Packaging : Our previous offline‑package tool only bundled entry files, so webpack‑generated chunks were omitted. We modified the tool to emit an entry JSON that lists non‑entry chunks, allowing the offline package to include them.
The new architecture diagram (see image in the original article) illustrates the overall flow.
With Hy 2.0, the app’s first‑screen rendering performance improved dramatically, and the same codebase can now operate as a SPA, a multi‑page app, or a hybrid solution, covering a wide range of business scenarios.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Qunar Tech Salon
Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.
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.
