-
Notifications
You must be signed in to change notification settings - Fork 424
React Compiler
Prepack has an embedded React compiler that allows for various ahead-of-time optimizations to React applications, with the aim of improving runtime performance.
To enable the React compiler, ensure that the reactEnabled
option is passed to the Prepack CLI (--reactEnabled
) or node entry point ({ reactEnabled: true }
).
There are other important configuration options related to the React compiler too:
-
By default, the serialization output of ReactElements is to
create-element
.create-element
output is the non-JSX form ofReact.createElement(SomeComponent, props, ...children)
. Alternatively, the ReactElement output can be set tojsx
, resulting in the a form of<SomeComponent {...props}>{children}</SomeComponent/>
.This can be configured via the CLI using
--reactOutput jsx
or the Node entry point{ reactOutput: "jsx" }
. -
Typically, React component
render
methods/functions are treated as pure functions and should have no observable side-effects to the global object. This feature is essential to better evaluate and handle React optimizations and be enabled usingabstractEffectsInAdditionalFunctions
.To enable this via the Prepack CLI use
--abstractEffectsInAdditionalFunctions
and for the Prepack Node entry point use{ abstractEffectsInAdditionalFunctions: true }
.Furthermore, with this feature enabled, you wrap other parts of your application in pure wrappers too – if they also have no global side-effects. To do this in your source code, use a special Prepack global function
__evaluatePureFunction(myPureFunction)
. -
Prepack expects a bundle that contains all the code for all parts of the application. Sometimes it's desirable to supply a partial bundle to Prepack where some modules might be peer dependencies (such as React, Redux, Relay etc) and not exist inside the bundle. In this case, these modules exist simply as external
require
calls.Setting the Prepack compatibility option to
node-react
will provide this functionality, which can be enabled via the Prepack CLI as--compatibility node-react
and for the Prepack Node entry point as{ compatibility: "node-react" }
.
The React compiler works by evaluating React component trees and by making intelligent optimizations to your React components. The React compiler needs to be told the "root" component of your application bundle, and once given that it should automatically figure out the entire component tree of components from that.
To tell the React compiler what is the root component for your application, you must use a Prepack global function called __optimizeReactComponentTree(MyRootReactComponent)
. For example in your sourecode (before bundling it and passing it to Prepack) you might do:
// MyComponent.js
import React from 'react';
class MyComponent extends React.Component {
render() {
return <span>Hello world!</span>;
}
}
// __optimizeReactComponentTree is only known to Prepack
// so we wrap it in a conditional so the code still works
// for local development testing without Prpeack
if (global.__optimizeReactComponentTree) {
__optimizeReactComponentTree(MyComponent);
}
module.exports = MyComponent;
The React compiler has a built-in React reconciler that attempts to "render" and evaluate your React application ahead-of-time. If it fails to, you'll typically be given an error/warning as to why this might not have happened. Please note: you must supply all your React components in the input bundle supplied to Prepack. The React compiler doesn't lookup require
or import
modules, nor does it attempt to access code from NPM or other repositories.
If you're a React library author, then supplying a complete bundle to Prepack with all peer dependencies (such as React, Relay, Redux etc) might not be desirable. Another approach is to provide Prepack with a "partial" bundle, where some modules are unknown ahead-of-time. This allows library authors to ship a library to their users with the benefits of Prepack but without the unnecessary need to include large frameworks in the bundle.
To do this, ensure that the following Prepack compatibility settings are set:
-
abstractEffectsInAdditionalFunctions
is enabled -
compatibility
isnode-react
-
reactEnabled
is enabled
Below is an example of how to use these effects on the output bundle with the above settings enabled:
// require all external modules, such as React, Relay and Redux
// note: "import/export" syntax isn't supported by Prepack at this stage
require("react")
require("react-relay");
module.exports = __evaluatePureFunction(function() {
// rest of your bundle code goes in here
// ensure the root module is returned
return whateverIsYourBundleModuleExports;
});
When you use the node-react
compatibility mode with Prepack, an internal mocked version of React is used to ensure that the React compiler is able to properly evaluate your code. So the below example works great:
// require all external modules, such as React, Relay and Redux
// note: "import/export" syntax isn't supported by Prepack at this stage
require("react")
module.exports = __evaluatePureFunction(function() {
var React = require("react")
function Child() {
return <span>Hello world</span>;
}
class App extends React.Component {
render() {
return <div><Child /></div>;
}
}
return __optimizeReactComponentTree(App);
});