How to Boost H5 Game Performance with Offline Packages and Smart Asset Management
This article details a comprehensive approach to improving the performance and user experience of complex H5 game-like applications by using offline package splitting, native splash screens, global state management with zustand, data prefetching, image loading optimizations, transition animations, and ranking list enhancements.
1. Offline Resources
Game‑style H5 features contain large static assets (>10 MB after compression) and many interactive scenarios. To avoid forcing users to download a full offline package when a diff version limit is reached, the offline package is split into a main bundle and sub‑packages (subpacks) that are loaded on demand.
1.1 Offline Package Splitting
Separate functionality into primary (first‑screen) and secondary (second‑screen) modules.
Place secondary modules under a subpacks directory.
Configure Webpack optimization.splitChunks to generate independent chunk files for each subpack and output them to publicPath/subpacks.
The offline‑package publishing platform provides packaging, diff generation and delivery for both main and sub‑packages.
The client downloads the main package first, then triggers sub‑package download via a JSBridge exposed to the front‑end.
1.2 Native Splash Screen
A native splash screen is displayed while the WebView loads the first DOM. The front‑end can dismiss it once the main UI is ready, eliminating the white‑screen flash.
{
"moduleName": "xx",
"url": ".+/xx",
"resID": "xx",
"resVersion": "1700720234678",
"loadingInfo": {
"loadingBgUrl": "https://xxx.png",
"loadingTextInterval": 1500,
"loadingText": ["xxxx"]
},
"packages": [{
"moduleName": "subpacks-xxx",
"resID": "xx",
"strategy": "open_block|preload",
"resVersion": "1700720234678"
}],
"versionControl": [{
"belowVersion": "xx",
"specificVersionList": [],
"minVersionName": "1.0.0",
"userNos": "xx"
}]
}1.3 Offline Package Loading Flow
The client first loads the main package, then uses the JSBridge to request sub‑packages according to the defined strategy. The native splash screen is hidden after the main DOM is rendered.
2. Data State Management and Prefetch
2.1 Global State Management with zustand
Using zustand a single store is created to hold data for all modules. Each module reads/writes through getters and setters, reducing duplicated network requests and simplifying data sharing.
// store.js
import create from 'zustand';
export default create((set, get) => ({
data: {},
async getData() {
try {
const res = await service.getData();
set({ data: res });
} catch (e) {
// error handling
}
},
setData: payload => set({ data: payload })
}));
// view.js
import useStore from './store';
const data = useStore(state => state.data);
const getData = useStore(state => state.getData);2.2 Data Prefetch for Secondary Modules
In the parent module’s useEffect the data for secondary modules is prefetched. When the secondary component mounts it can use the prefetched data, falling back to a fresh request if the cache is missing. This works with React‑Router v6 loaders and React Suspense.
useEffect(() => {
fetchData(A1); // primary module data
prefetchData(A2); // secondary module A2
prefetchData(A3); // secondary module A3
}, []);
// In A2 / A3 components
const data = useStore(state => state.dataA2);
return <A2 data={data} />;3. Image Loading Optimization
3.1 Static Image Preload via Web Worker
Large background images for secondary modules are preloaded in a Web Worker, keeping the main thread free and caching the assets offline.
// preloadAssets.js
import { RESOURCE_TYPE } from '@music/tl-resource';
import BoxBg from '@/subpacks/assets/TreasureBox/tbg.png';
import PackageBg from '@/subpacks/assets/PackStore/bg.png';
export default [
{ src: BoxBg, type: RESOURCE_TYPE.IMAGE },
{ src: PackageBg, type: RESOURCE_TYPE.IMAGE },
// …other images
];
// In a component
await Resource.loadResource(loadAssets, progress => {
setLoadProgress(progress);
});3.2 Dynamic Image Handling
PGC images are sized on the backend to match UI design (e.g., 300 × 300 for a 100 × 100 element). Frequently used PGC assets are localized and mapped to storage keys; UGC images are served via CDN with cropping parameters. Prefer <img> over background‑image to improve loading priority.
// Local image map
const LocalImgMap = {
'obj_w57DlM...': require('./locals/obj_w57DlM...png'),
// …more mappings
};
const LocalImg = ({ src, ...rest }) => {
const key = Object.keys(LocalImgMap).find(k =>
src.includes(k.replaceAll('_', '/'))
);
const finalSrc = LocalImgMap[key] || src;
return <Image src={finalSrc} {...rest} />;
};4. Transition Animation Effects
Apply subtle CSS or animation‑library transitions when UI updates are triggered by data changes (e.g., fade‑out of list items, slide‑in of new content). For full‑screen scene switches embed a pre‑rendered animation that runs before the next scene is displayed. Standardize interaction feedback such as button click ripple, modal show/hide, and toast animations to give the H5 a game‑like feel.
5. Ranking List Optimization
Complex ranking pages may contain multiple nested tabs (e.g., 2 × 3 × 2 = 12 lists). Rendering all items on each tab switch causes UI flicker and performance loss.
// Original implementation (causes flicker)
<Fragment>
<Tabs tabs={[A1, A2]} />
<Tabs tabs={[B1, B2, B3]} />
<Tabs tabs={[C1, C2]} />
<List data={calc(A1, B1, C1)} />
</Fragment>Replace the list with a KeepAliveList that keeps three list nodes in memory and shows only the active one. Switching tabs moves the cached list into view without recreating DOM nodes.
// KeepAliveList usage
<KeepAlive cacheKey={`${biz}_pre`} saveScrollPosition={false}>
<div className="item hide" key={pre}>{childs[pre]}</div>
</KeepAlive>
<div className="item show cur" key={index}>{childs[index]}</div>
<KeepAlive cacheKey={`${biz}_next`} saveScrollPosition={false}>
<div className="item hide" key={next}>{childs[next]}</div>
</KeepAlive>During scene entry, prefetch the first ten items of each ranking list to avoid an initial loading spinner. Subsequent items are loaded incrementally (e.g., infinite scroll).
Summary and Future Work
Combining offline package splitting, native splash screens, global state management with zustand, data prefetch, static image preloading via Web Worker, dynamic image mapping, transition animations, and KeepAliveList for ranking pages significantly improves perceived performance and user experience of game‑style H5 applications. Future work includes extracting these patterns into reusable libraries to reduce development overhead for similar projects.
References
react‑activation
Web Worker usage in projects
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.
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.
