Webpack

If you come from Gulp — or Grunt — Webpack basically does the same things: it takes your files, runs them through some sort of conversion/transpiler system and spits out something the browser can understand.

For example, with Gulp, we can add a SASS package to our pipe to get the CSS output that we expect. That’s very similar to what Webpack does. In fact, because they both are so similar, there should not be an instance where you would use both together — unless you have a Gulp process already in place and you do not want to give up on it. So, because Webpack can easily replace Gulp, I do believe that we can in fact define it as a build system.

Aside of bundling all your resources, Webpack, if configured right, can do much more. For instance, with Webpack we can:

  • Watch for file changes and re-run tasks
  • Run Babel transpilation to ES5
  • Run a developer web server
  • Use auto-prefixes
  • Use require() for CSS files.
  • Tree Shaking (Dead code elimination) — that basically means that Webpack can find code that you are not actually using and not add it to the final bundle.
  • Code Splitting (Lazy loading of modules) — Bundle up smaller modules of JavaScript so we are not serving your entire application to every single user that just visits your homepage.

The Webpack configuration file is usually stored in a file named Webpack.config.js in the project root folder. On that configuration file we will tell Webpack the entry point of our application, the output file that should be generated once everything is bundled and any other additional rules or tasks we might want it to run.

Webpack.config.js

module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js'
}
};

With all that in place, all we need is to reference the bundle.js file in our HTML page. When we run webpack on the command-line, a file called bundle.js will be created with all of our resources bundled.

Webpack vs Gulp (using Example)

You set up a basic JavaScript function that gets called and executed. It’s so simple that many people have extended it beyond its initial intend.

Webpack, on the other hand, is all about bundling. That entails a more complex syntax, as I previously mentioned, as well a more complex approach for cases such as when you need your modules to be on their own files or if you need your CSS to be taken out of that bundle, as things like these are not core to Webpack — there are plugins to do all those things and more, but what you end up with is getting stuck in the spider web of this massive configuration file that you can’t easily break it or give it to the next person to understand it.

Now, let’s compare the implementation of a simple task to see how Gulp and Webpack syntax differ. The simplest thing we can do is to convert our LESS or SASS files into CSS files. With Gulp, that’s incredible simple:

'use strict';

var Gulp = require('Gulp');
var sass = require('Gulp-sass');

Gulp.task('sass', function () {
return Gulp.src('./sass/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(Gulp.dest('./css'));
});

Gulp.task('sass:watch', function () {
Gulp.watch('./sass/**/*.scss', ['sass']);
});

With Webpack, because it really just supports Javascript bundling, we have to compile our SASS/LESS and CSS files into a JavaScript file. Once that file is compiled, we can use a plugin to extract the CSS text from the JavaScript file and put it on its own file.

const ExtractTextPlugin = require('extract-text-Webpack-plugin');const extractSass = new ExtractTextPlugin({
filename: "[name].[contenthash].css",
disable: process.env.NODE_ENV === "development"
});
module.exports = {

module: {
rules: [{
test: /\.scss$/,
use: extractSass.extract({
use: [{
loader: "css-loader"
}, {
loader: "sass-loader"
}],
// use style-loader in development
fallback: "style-loader"
})
}]
},
plugins: [
extractSass
]
};

As we can see, Gulp is extremely simple, as Webpack is more complex and require plugins and rules to execute the tasks we need. However, on a regular project, there are many more tasks involved other than just converting SASS/LESS files into CSS files. That’s when developers starting using Gulp to do more things, such as bundling files, which is the main purpose of Webpack. That might be fine, because you start small and scale your code as you need, but because you need to be constantly scaling up your Gulp file, that can become a cumberstone task and you might end up pushing Gulp too far from it’s main purpose. Things like error catching and reloading or running tasks in a certain order can turn your Gulp file into a monster on its own — things like these are not supported in Gulp, so you actually need packages such as plumber and run-sequence to achieve those tasks. That’s why Webpack is so powerful. Even though it’s syntax might be complex, once you understand it, it can become a very amazing tool that allows you to do a lot of the things you would struggle with Gulp. Again, with Gulp, when you add complexity, you are basically pushing its boundaries, as Webpack is actually meant for that kind of environment.

If you just need some basic bundling, just stick with Gulp, you don’t need to bother with Webpack. However, if you need to mix in multiple JavaScript files and maybe have some of them split up in certain ways or if you need to have jQuery and Bootstrap split out but still want to share them between files, or if you need multiple HTML files, then stop using Gulp and look at Webpack.

Webpack is an incredible, powerful tool that can do quite a lot for you when you are dealing with a massive application. For instance, one of the things it does very well it’s called tree shaking. Let’s say, for example, that you only need a couple of functions from a massive library like lodash. You can actually leverage Webpack tree shaking and only grab the functions you need, without needing to load the entire library. Again, if you are using Gulp and you get yourself needing more and more advanced features, that’s when you should switch to Webpack.

So, if you ask me, is Webpack worth it? Absolutely, it’s the best build system around even if you don’t want to call it a build tool or build system. It handles modern JavaScript applications in a way that you could not necessarily do before with all of the modern bells and whistles. But at the end of the day, you will have to decide which one is better for you based on your situation and on our project. Regardless of your choice, both Webpack and Gulp are amazing tools and have a great community behind them.

Let me know in the comments below which one you have been using, I would love to hear what people have to say about both systems. And remember, stay curious and keep coding!

Bundling

If you’re using Create React App, Next.js, Gatsby, or a similar tool, you will have a Webpack setup out of the box to bundle your app.

Code-Splitting

Code-splitting your app can help you “lazy-load” just the things that are currently needed by the user, which can dramatically improve the performance of your app. While you haven’t reduced the overall amount of code in your app, you’ve avoided loading code that the user may never need, and reduced the amount of code needed during the initial load.

There are three general approaches to code splitting available:

  • Entry Points: Manually split code using entry configuration.
  • Prevent Duplication: Use the SplitChunksPlugin to dedupe and split chunks.
  • Dynamic Imports: Split code via inline function calls within modules.

Entry Points

project

webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
|- index.js
+ |- another-module.js
|- /node_modules

another-module.js

import _ from 'lodash';console.log(
_.join(['Another', 'module', 'loaded!'], ' ')
);

webpack.config.js

const path = require('path');module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
+ another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};

This will yield the following build result:

...
Asset Size Chunks Chunk Names
another.bundle.js 550 KiB another [emitted] another
index.bundle.js 550 KiB index [emitted] index
Entrypoint index = index.bundle.js
Entrypoint another = another.bundle.js
...

As mentioned there are some pitfalls to this approach:

  • If there are any duplicated modules between entry chunks they will be included in both bundles.
  • It isn’t as flexible and can’t be used to dynamically split code with the core application logic.

The first of these two points is definitely an issue for our example, as lodash is also imported within ./src/index.js and will thus be duplicated in both bundles. Let's remove this duplication by using the SplitChunksPlugin.

read more @ https://webpack.js.org/guides/code-splitting/

Dynamic Imports

source: https://lecstor.com/code-tree-webpack/

import loadable from '@loadable/component';const Page1 = loadable(() => import('./page1'));
const Page2 = loadable(() => import('./page2'));
const Page3 = loadable(() => import('./page3'));
...export default App;

Now, webpack will split our app bundle into four bundles..

dist/
├── [161K] main.bundle.js
├── [ 441] pages-page1.bundle.js
├── [ 441] pages-page2.bundle.js
└── [ 304] pages-page3.bundle.js

the syntax is not as concise and nice as in Gulp — where you basically write functions and everything is easily trackable — but instead filled with a lot of jargon terms like loaders, modules, rulers, which can be intimidating and hard to pick up. That’s not to say Webpack is unreadable, but the learning curve can be steep. On a bright side, the documentation on Webpack’s website is great and it’s definitely the first stop for someone learning the ins and outs of Webpack.

Why Are Developers Using Webpack Instead of Gulp?

Simply put, Webpack is such a powerful tool that it can already perform the vast majority of the tasks you’d otherwise do through a task runner. For instance, Webpack already provides options for minification and sourcemaps for your bundle. In addition, Webpack can be run as middleware through a custom server called webpack-dev-server, which supports both live reloading and hot reloading (we’ll talk about these features later). By using loaders, you can also add ES6 to ES5 transpilation, and CSS pre- and post-processors. That really just leaves unit tests and linting as major tasks that Webpack can’t handle independently. Given that we’ve cut down at least half a dozen potential gulp tasks down to two, many devs opt to instead use NPM scripts directly, as this avoids the overhead of adding Gulp to the project (which we’ll also talk about later).

The major drawback to using Webpack is that it is rather difficult to configure, which is unattractive if you’re trying to quickly get a project up and running.

◉ Code Splitting in React using create-react-app

🔘 import()

Before:

import { add } from './math';console.log(add(16, 26));

After:

import("./math").then(math => {
console.log(math.add(16, 26));
});

🔘 React.lazy

Note:

React.lazy and Suspense are not yet available for server-side rendering. If you want to do code-splitting in a server rendered app, we recommend Loadable Components. It has a nice guide for bundle splitting with server-side rendering.

The React.lazy function lets you render a dynamic import as a regular component.

Before:

import OtherComponent from './OtherComponent';

After:

const OtherComponent = React.lazy(() => import('./OtherComponent'));

This will automatically load the bundle containing the OtherComponent when this component is first rendered.

React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component.

The lazy component should then be rendered inside a Suspense component, which allows us to show some fallback content (such as a loading indicator) while we’re waiting for the lazy component to load.

import React, { Suspense } from 'react';const OtherComponent = React.lazy(() => import('./OtherComponent'));function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}

The fallback prop accepts any React elements that you want to render while waiting for the component to load. You can place the Suspense component anywhere above the lazy component. You can even wrap multiple lazy components with a single Suspense component.

import React, { Suspense } from 'react';const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}

🔘 Error boundaries

import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
const MyComponent = () => (
<div>
<MyErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</MyErrorBoundary>
</div>
);

🔘 Route-based code splitting

A good place to start is with routes. Most people on the web are used to page transitions taking some amount of time to load. You also tend to be re-rendering the entire page at once so your users are unlikely to be interacting with other elements on the page at the same time.

Here’s an example of how to setup route-based code splitting into your app using libraries like React Router with React.lazy.

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);

🔘 Named Exports

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

References:

Experience with Front-end Technologies and MERN / MEAN Stack. Working on all Major UI Frameworks like React, Angular.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store