hakunin
hakunin

Reputation: 4231

How to optimize webpack/es6 overhead?

I've been optimizing the load time of my app and after some quick wins with optimizing my code I've noticed there seems to be a 500ms long intialization phase where all the require statements seems to be resolved, or something.

Can this be optimized and how?

I am using webpack, react and couple dozens of npm packages. The result file is 2.8M unzipped and around 900k zipped. There is not a huge amount of code of the app itself, its mostly npm packages.

I wonder if I can just compile this differently to avoid all the requires and what not in real time.

Update: I am using production build with dedupe plugin currently.

Webpack/es6 overhead

Upvotes: 12

Views: 3334

Answers (3)

Johannes Ewald
Johannes Ewald

Reputation: 17805

As some comments have already pointed out, we have to differentiate between build- and run-time. The referenced guide from the webpack docs is about build performance.

While not using devtools like source maps and minifying code has impacts on the execution speed, I think the most important one is chunking/lazy loading (as estus has already pointed out).

You should decide which parts of your application are not needed for the initial render. These parts should be moved into another chunk and loaded lazily via require.ensure().

Usually your router is a typical place to load stuff asynchronously:

<Router>
    <Route
        path="/dashboard"
        getComponent={loadPage("Dashboard")}
    />
</Router>

function loadPage(page) {
    return (location, cb) => pages[page](cb);
}

const pages = {
    Dashboard(cb) {
        require.ensure(
            ["./Dashboard.js"],
            require => cb(null, require("./Dashboard.js").default)
            // .default is required in case you are using ES2015 modules
        );
    }
};

Now, all modules that are only needed on the Dashboard will be moved into a separate chunk.

The verbose require.ensure part can even be optimized by moving all top-level components (I call them pages here) into a dedicated folder like pages. Then you can configure webpack to always load these things asynchronously by using the bundle-loader:

// webpack.config.js
...
{
    test: /\.jsx$/,
    include: [
        path.resolve("path", "to", "pages"),
    ],
    loaders: [
        "bundle-loader?" + JSON.stringify({
            lazy: true
        })
    ]
},

Then your router looks like:

// This does not actually import the Dashboard,
// but a function which loads the dashboard.
import Dashboard from "path/to/pages/Dashboard";

function loadPage(page) {
    return (location, cb) => pages[page](module => {
        cb(null, module.default);
    });
}

const pages = {
    Dashboard
};

And if you're super-lazy and you only want to refer to the same filename without creating a pages-Object manually, you can also use require contexts:

function loadPage(page) {
    return (location, cb) =>
        require("./path/to/pages/" + page + ".jsx")(
            module => cb(null, module.default)
        );
}

Upvotes: 3

Everettss
Everettss

Reputation: 16029

I don't believe that your timeline comes from minify code (compare __webpack_require__ in maped files and f in minify code).

I will show that minify and some plugins can reduce two times running time of this script. In webpack configs I will show only the main difference between to configurations.


Develop mode

webpack.config.js

devtool: 'cheap-module-eval-source-map',
cache: true,
debug: true,

Timeline - 473 ms

timeline in develop mode

Production mode

webpack.config.js

plugins: [
    'transform-react-remove-prop-types',
    'transform-react-constant-elements',
    'transform-react-inline-elements'
],
cache: false,
debug: false,
plugins: [
    // Search for equal or similar files and deduplicate them in the output
    // https://webpack.github.io/docs/list-of-plugins.html#dedupeplugin
    new webpack.optimize.DedupePlugin(),

    // Minimize all JavaScript output of chunks
    // https://github.com/mishoo/UglifyJS2#compressor-options
    new webpack.optimize.UglifyJsPlugin({
        compress: {
            screw_ie8: true, 
            warnings: false,
        },
    }),

    // A plugin for a more aggressive chunk merging strategy
    // https://webpack.github.io/docs/list-of-   plugins.html#aggressivemergingplugin
    new webpack.optimize.AggressiveMergingPlugin(),
]

Timeline - 228 ms

timeline in production mode


If you would like to analize in depth webpack.config.js from this explanation you can take a look at source code from react-starter-kit.

Upvotes: 2

Chris
Chris

Reputation: 8020

One thing you could do is to play around with the devTool config and change the way you generate your sourcemaps. That should speed things up a bit at the expense of ease of debugging.

Webpack actually has a great little guide on how to optimise performance here http://webpack.github.io/docs/build-performance.html.

Basically it boils down to how much debugging information you feel you need.

By setting

{
  devtool: "#source-map"
}

You preserve the original code, which is obviously the easiest to debug, but this comes at the expense of files size / build time.

Updated: As per Chris' comment below, here is another guide

Upvotes: 2

Related Questions