Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 767
hakunin Avatar asked Jun 20 '16 10:06

hakunin


3 Answers

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)
        );
}
like image 137
Johannes Ewald Avatar answered Nov 12 '22 15:11

Johannes Ewald


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

like image 30
Chris Avatar answered Nov 12 '22 16:11

Chris


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.

like image 2
Everettss Avatar answered Nov 12 '22 15:11

Everettss