diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 6070de8..49c0b55 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -46,6 +46,9 @@ const config = { '2.x': { label: '2.x', }, + '3.x': { + label: '3.x', + }, }, }, blog: { diff --git a/docs/versioned_docs/version-3.x/api/_category_.json b/docs/versioned_docs/version-3.x/api/_category_.json new file mode 100644 index 0000000..bd503d0 --- /dev/null +++ b/docs/versioned_docs/version-3.x/api/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "API reference", + "position": 4, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-3.x/api/is-cached.md b/docs/versioned_docs/version-3.x/api/is-cached.md new file mode 100644 index 0000000..ddd19aa --- /dev/null +++ b/docs/versioned_docs/version-3.x/api/is-cached.md @@ -0,0 +1,13 @@ +--- +sidebar_position: 3 +--- + +# isCached + +### `isCached` - check if screen/component was already preloaded + +Call this function if you need to check whether the component was already loaded or not: + +`isCached(componentName: string)` => `boolean` + +- `componentName` - name of the component/screen that was registered in `register` function diff --git a/docs/versioned_docs/version-3.x/api/preload.md b/docs/versioned_docs/version-3.x/api/preload.md new file mode 100644 index 0000000..a9e7c80 --- /dev/null +++ b/docs/versioned_docs/version-3.x/api/preload.md @@ -0,0 +1,16 @@ +--- +sidebar_position: 2 +--- + +# preload + +### `preload` - as an optimization, you can also decide to preload a component before it gets rendered. + +You can select what you'd like to load: +- `preload().component(componentName: string)` => `Promise` + + - `componentName` - name of your component, that was registered via `register` function + +- `preload().group(groupName: string)` => `Promise` + + - `groupName` - name of your group, that was specified in `register` function diff --git a/docs/versioned_docs/version-3.x/api/register.md b/docs/versioned_docs/version-3.x/api/register.md new file mode 100644 index 0000000..5763adb --- /dev/null +++ b/docs/versioned_docs/version-3.x/api/register.md @@ -0,0 +1,18 @@ +--- +sidebar_position: 1 +--- + +# register + +### `register` - allow you wrap your component/screen into separate bundle + +`register

(params: Object)` - return react component (`P` - component props type, use only in TypeScript projects). + +Params: +- `name` (optional) - you need to specify this param only if you will call `preload` function for this screen +- `require` - function, that return your presentation component. Example: `() => require('./Presentational.js')` (**DEPRECATED - will be removed in future releases. Use `loader` instead**) +- `loader` - function, that return your presentation component. Example: `() => import('./View')` +- `group` (optional) - You need specify it, only if you need to `preload` entire group of screens +- `static` (optional) - all static members from your presentational component. +- `cached` (optional) - Default: `true`. Set to `false` if you don't need cache your screens. _**Warning:**_ it may decrease performance of your application. +- `placeholder` (optional) - React component which will display during screen loading. Should specify if you don't use `preload` for this screen. diff --git a/docs/versioned_docs/version-3.x/fundamentals/_category_.json b/docs/versioned_docs/version-3.x/fundamentals/_category_.json new file mode 100644 index 0000000..ea8044f --- /dev/null +++ b/docs/versioned_docs/version-3.x/fundamentals/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Fundamentals", + "position": 1, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-3.x/fundamentals/basic-usage.md b/docs/versioned_docs/version-3.x/fundamentals/basic-usage.md new file mode 100644 index 0000000..83a3921 --- /dev/null +++ b/docs/versioned_docs/version-3.x/fundamentals/basic-usage.md @@ -0,0 +1,68 @@ +--- +sidebar_position: 3 +--- + +# Basic usage + +This library contains pretty minimalistic API. And we will look on this a little bit later. +Let's assume we have next folder structure + + . + ├── ... + ├── Login # Folder with your screen + │ ├── View.jsx # Your presentational component + │ ├── styles.ts # Your styles for the component + │ └── index.ts # Your entry point to the screen + └── ... + +And your `index.ts` file looks like this: + +```js +import View from './View'; + +export default View; +``` + +You can easily rewrite it with this library! + +```js +import { register } from 'react-native-bundle-splitter'; + +export default register({ loader: () => import('./View') }); +``` + +That's all! You can reload your application to see the results and guarantee this changes will not break anything. + +:::tip +If you are using TypeScript in your project, then you may want to specify `Props` for your lazily loaded component. You can do it in this way: + +```ts +import { register } from 'react-native-bundle-splitter'; +import type {Props} from './types'; + +export default register({ loader: () => import('./View') }); +``` +::: + +:::caution Avoid direct references to lazy-loaded file +Please, be sure, that you have such `index.ts` file. This library works only in case if you don't have any `import` statements which refers to your file. In other words: be sure that you do **NOT** import this file (`View`) from anywhere in your code. +::: + +To assure, that you are doing everything in correct way this library provides a way for checking the performance. + +In order to capture your metrics you should change your `App.js` file: + +```diff +import React, { Fragment, PureComponent } from 'react'; +... ++import { investigate } from 'react-native-bundle-splitter/dist/utils'; + ++console.log('Bundle Info: ', investigate()); + +class App extends PureComponent { +... +} +``` + +Usually your output from log will look like `Bundle Info: { loaded: 2731, waiting: 141 }`. Such way allows to understand is your changes make sense or not. After adding new screen to this library count of loaded files should be decreasing. Of course, ideal way is when you load as minimum modules as you can. For example only one page with login. In this case your startup time will be almost the same as just created application. And splash screen will not take a lot of time. +But it depends absolutely on you and yours architecture solutions. Feel free to play around this library and find suitable way for separating your files. diff --git a/docs/versioned_docs/version-3.x/fundamentals/enabling-ram-bundle.md b/docs/versioned_docs/version-3.x/fundamentals/enabling-ram-bundle.md new file mode 100644 index 0000000..c69f1b9 --- /dev/null +++ b/docs/versioned_docs/version-3.x/fundamentals/enabling-ram-bundle.md @@ -0,0 +1,89 @@ +--- +sidebar_position: 2 +--- + +# Enabling Ram Bundle + +## RAM... What is it? About RAM Bundle format + +### RAM Bundle format + +[Metro](https://facebook.github.io/metro/) bundler for react-native offers special format - RAM (Random Access Memory). RAM bundle is a new format of React Native application packaging that helps to optimize load times of your app. + +You can read [more](https://facebook.github.io/metro/docs/bundling) about this bundle format. + +### Context and motivation + +React Native is a still JS application. In the browser all your JavaScript and modules have to be bundled (combined, minified, and maybe even uglified) before deploying. As a result, you drastically reduce the number of files (usually down to one) along with the total size of the code you’re serving by removing all information not necessary for production, i.e., mercilessly rewriting function and variables names, deleting code comments, and so on. + +The whole procedure stays pretty much the same when you’re developing a React Native app. It’s still (React-flavored) JavaScript you write there, right? + +But here’s a problem: when your app grows, so does the bundle size, and let’s just say that React Native apps are usually more than a few kilobytes in size. The heavier your app bundle is, the more time it needs to be fully loaded into memory, parsed, and executed, before even showing you a splash screen! + +However, the difference is that a React Native app is executed in a different environment — on your mobile device running Android or iOS (coming soon on Windows devices) rather than in your browser. This allows React Native to be smarter about what it loads to memory and when. And this is when RAM bundles come into play: they define a new bundling format that enables on-demand loading of individual app modules, resulting in a smoother user experience. + +### Formats + +The official way to bundle your React Native apps at the moment is using Metro Bundler, which currently supports the following bundling formats: + +- **Plain**: Good old, pure JavaScript bundle. This is the default type that doesn’t offer any optimizations. + +- **Indexed RAM Bundle**: This format defines a binary file with a header that can be used for quickly finding a specific module. For example, the header can tell us that module number 23 (all JavaScript modules in React Native apps are given numerical identifiers) can be found at offset 320 in the RAM bundle file, and has a length of 430 bytes. How does this help us? React Native runtime now doesn’t need to read the entire bundle file into memory, and can load specific modules only when they are needed (e.g., via inline requires). + +- **File RAM Bundle**: With this approach, every module is stored in a separate file with the name `js-modules/${id}.js`, where `${id}` is the module’s ID. This format is used by default on Android devices but has the same purpose: to have fast access to individual modules in your bundle. + +## Enabling RAM Bundle feature in your application + +For enabling this format in your application you need to do pretty simple steps for each platform. + +:::tip Enable per platform +Although enabling RAM Bundle format is recommended for both platforms, you can only enable it for one if necessary. +::: + +### Android + +Open `android/app/build.gradle` and edit your file in the following way. + +```diff +project.ext.react = [ + entryFile : "index.js", ++ bundleCommand : "ram-bundle", ++ extraPackagerArgs : ["--indexed-ram-bundle"] + ... +``` + +:::info +You can choose between formats, since Android support both format (indexed and file). But since iOS support only `indexed` format, would be reasonable to keep both platform in consistent and use `indexed` format. But if you decided for some reasons to use `file` format, you don't need to add `extraPackagerArgs: ["--indexed-ram-bundle"]` line. By default android uses `file` format. +::: + +:::caution Hermes enabled +If you are trying to enable this feature with Hermes engine, you may faced with application crash. It's a known [issue](https://github.com/facebook/react-native/issues/25730). As a temporary solution you can don't activate this bundle format for Android, since Hermes is using similar [mechanism](https://github.com/facebook/react-native/issues/25730#issuecomment-514115115). +::: + +### iOS + +For enabling RAM format for iOS you need to open XCode, select the project, go to `Build Phases`, and edit phase `Bundle React Native code and images`. Before `../node_modules/react-native/scripts/react-native-xcode.sh` you need to add `BUNDLE_COMMAND="ram-bundle"`: + +```diff ++export BUNDLE_COMMAND="ram-bundle" + export NODE_BINARY=node + ../node_modules/react-native/scripts/react-native-xcode.sh +``` + +### macOS + +Enabling `RAM Bundles` on macOS should be done exactly in the same way as for `iOS` + +Just open macOS specific project and repeat the same steps. + +### web + +No extra steps for `web` as there is no such term as "RAM bundles". Webpack itself will be able to split your JS bundle into small parts and will load them as soon as they are needed. + +## Summary + +Basically you have completed native set up and now you are ready for using this library! + +:::tip +To be sure, that your application isn't broken after all manipulations you can rebuild your application and see, that it works correctly. +::: diff --git a/docs/versioned_docs/version-3.x/fundamentals/getting-started.mdx b/docs/versioned_docs/version-3.x/fundamentals/getting-started.mdx new file mode 100644 index 0000000..45432fd --- /dev/null +++ b/docs/versioned_docs/version-3.x/fundamentals/getting-started.mdx @@ -0,0 +1,53 @@ +--- +sidebar_position: 1 +--- + +# Getting started + +## What to expect + +Splitting your bundle into small pieces allows you reduce size of main bundle, which is loaded on application startup. It means, that you can load only necessary things, such as login pages, which user inevitably will see if open an application. And only "necessary" things will be in main bundle. Thus, this means that **the time of initial launch of the application will be minimized** and **memory consumption will be decreased**, since often in applications is a lot of code (components and screens) that the user may simply not see. + +import BundleSplittingImageUrl from "@site/static/img/bundle-splitting.png"; + +

+ + + On green part of this picture, you can see that all components/screens are + divided into different groups. It allows you to load the application much + faster, because instead of loading the entire bundle (red picture), you can + load only the parts that you need. + +

+ +## What does this package do? + +This package is built on top of **RAM** bundles. In addition to the base functionality provided by React Native, it allows you to: + +- avoid boilerplate code (you don't need to write code as per react-native docs - you can simply wrap your component into `register` function); +- use `preload` API (when you can load screens in advance and the transition to them will be as if the entire bundle was loaded at once); +- and has web support. + +## Features + +This library is add-on on react-native API. But this library has some features, and here is list of them: + +- **Enhanced cache management**. This library provide a way for caching yours components. This mechanism allows you to improve performance of your application and help to avoid unnecessary reloading yours components, which were already loaded. +- **Ability to preload component**. You can preload components in background, which user may see in the nearest future. It allows to make UI experience more smooth and use your components from cache without any intermittentions. +- **Supporting all navigation libraries**. This library is compatible with all most known navigation libraries, such as [react-navigation](https://reactnavigation.org/), [react-native-navigation](https://wix.github.io/react-native-navigation/#/) and more others. + +## Installation + +Install the react-native-bundle-splitter package in your React Native project. + +```bash +yarn add react-native-bundle-splitter +# or with npm +# npm install react-native-bundle-splitter --save +``` + +This module does not use any native (platform) dependencies and does not need to be linked. So installation process basically is finished. But you also need to enable `RAM bundles` feature in your application. To see how to do it, please read [the guide](./enabling-ram-bundle). + +:::tip Minimal react-native version +You need to use react-native 0.60 or higher, since feature with inline requires is available out-of-box only from this version. +::: diff --git a/docs/versioned_docs/version-3.x/guides/_category_.json b/docs/versioned_docs/version-3.x/guides/_category_.json new file mode 100644 index 0000000..b07efb9 --- /dev/null +++ b/docs/versioned_docs/version-3.x/guides/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Guides", + "position": 2, + "link": { + "type": "generated-index" + } +} diff --git a/docs/versioned_docs/version-3.x/guides/async-loading.md b/docs/versioned_docs/version-3.x/guides/async-loading.md new file mode 100644 index 0000000..732532f --- /dev/null +++ b/docs/versioned_docs/version-3.x/guides/async-loading.md @@ -0,0 +1,131 @@ +--- +sidebar_position: 3 +--- + +# Async loading + +If you did not load all the screens at the stage of opening the application, then, of course, you need to load them after (for example, if the user logs in and you need to redirect him to another screen, which has not yet been loaded). + +This library will load it automatically and cache, if it's needed. However, it can lead to application freezes or the appearance of “white blinks”. + +To avoid this, you can load the necessary screens asynchronously. + +## One specific screen + +More often you need this library when you have any async operations, between navigating. Let's have a look at the next example: + +```js +// home.ts +import React from 'react'; +import { Button, View, Text } from 'react-native'; + +const REGISTERED = 'REGISTERED'; + +const doLoginRequest = (): Promise<{ status: string }> => new Promise((resolve) => { + setTimeout(() => resolve({ status: REGISTERED }), 3000); +}) + +class HomeScreen extends React.Component { + render() { + return ( + + Login +