Building Web Apps Fast with Webpack

Building Fast Web Apps with Webpack

Before we dig into the techy details, let’s start with an excerpt from our previous blog on Webpack. From The Business Case for Coding with Webpack:

Transpilers were initially intended as a temporary measure. “These features will one day be supported in your browser by default” many JavaScript and CSS documentation pages boldly declare. But, if we’re honest with ourselves, the transpiler is here to stay.

For example, only a fraction of the ES2015 standard has been implemented in most major browser versions, and we’re most of the way through 2016. Meanwhile, people are already starting to talk about ES2017. This really isn’t a new paradigm in software. If we had to write user interfaces in machine code, we would still be reading email in a terminal and viewing images in 16-bit color. Instead, we invented compilers that translate non-standard, unoptimized (but fully featured) code into standard, optimized code.

Why is this important? I’m proposing that the same thing is happening in the JavaScript world and the new compiler is Webpack. Just as writing an Android app in Java is more time efficient than writing it in C, so writing websites in next-generation JavaScript with tools like Webpack is more efficient than writing them in current-generation JavaScript.

Why Webpack?

It is important to note, that there are many tools for transpiling JavaScript and CSS, linting, unit testing, and even integrating them all. Several of these tools have achieved wide community support such as gulp and browserify. But there are two things that set Webpack apart from them all.

Optimized Development Workflow and Output

Webpack is heavily optimized by default. You won’t run into issues of Webpack duplicating code on accident, neither does Webpack require excessive amounts of complex programming up-front. Everything is optimized to just work. You don’t even have to set up a development server, Webpack has a standard one called Webpack Dev Server (webpack-dev-server) that requires no additional configuration. The second thing that sets Webpack apart is speed. Because of the heavy optimization that Webpack encompasses, it can compile in a fraction of the time of Browserify, or even most custom gulp build systems.

Batteries Included: Webpack Dev Server

Webpack Dev Server is a lightweight Node JS server that installs in seconds, runs on any computer, and serves you compiled project files out of a memory cache. Whenever anything in your source code changes, Webpack re-compiles your app in memory, and even automatically refreshes your browser window for you. Since Webpack Dev Server is serving your content out of a memory cache instead of the file system, it can reduce compile time significantly. Re-compiling even a sizeable project can be almost instantaneous. You can even set up a feature called “hot reloading”, which updates your app with any source changes (only the modules that changed) without refreshing your screen, or resetting your JavaScript state. This preserves your app and console state, allowing for extremely efficient debugging. (If you’re a fan of React JS, you’ll love this). Webpack Dev Server is absolutely essential to getting the full effect Webpack has to offer, and can increase development speed even further.

Getting Started with Webpack

So how do you get started developing with Webpack? You simply need to configure Webpack to inform it of your existing workflow. At the beginning of each project, you setup Webpack to use your favorite tools such as transpilers, unit testing frameworks, or linters. Since Webpack’s configuration can be entirely source controlled, it can be automatically exported to all of the contributors on a given project.

Modify an Existing Configuration

The best way to learn how to configure Webpack is to tweak an existing Webpack setup. I’ve created a basic Webpack configuration for you to play with, you can get it on Github. Just follow the installation instructions in the readme, and you should have a basic Webpack environment running in less than 15 minutes. The webpack.config.js file in the root directory contains all of the necessary configuration to make Webpack work. The config file specifies an entry point, a base JavaScript file that requires all of your project resources, and an output file and folder, where Webpack will deposit your bundled resources. The Html Webpack Plugin automatically injects a reference to that bundle into your index.html file. Additional tools (such as the Babel transpiler) are configured by adding an entry to the “loaders” section. This specific example compiles ES6 and JSX for ReactJS, as well as Sass for styles—all technologies that we use frequently at CQL. I’m also automatically importing all fonts associated with the project to the /dist/fonts folder (even fonts included from external libraries or other locations). Feel free to tweak or replace these loaders to match your favorite tools and workflow.

loaders: [

{ // CSS/Sass loader config

test: /.s?css$/,

loaders: [‘style’, ‘css’, ‘postcss’, ‘sass’]

},

{ // ES6 loader config

test: /.jsx?$/,

exclude: /(node_modules|bower_components)/,

loaders: [‘babel’]

},

{ // Import fonts

test: /.(eot|svg|ttf|woff|woff2)(?S*)?$/,

loaders: [‘file?name=fonts/[name].[ext]’]

}

]

What About CSS?

One oddity that you might notice in my example, is that no CSS file is being exported by default, but is rather being included via a “require” statement in JavaScript (the very top of src/index.js). Importing your styles this way is critical to get Webpack’s hot reloading features working, tools that can dramatically speed up the development process. Of course you never want to do this in production, so you’ll want to configure the Extract Text Webpack Plugin (extract-text-webpack-plugin) when exporting for production.

new HtmlWebpackPlugin({

title: ‘Webpack Build’,

template: ‘./src/index.html’

})

The Extract Text Webpack Plugin will output any required CSS files to an external CSS file instead of loading it with JavaScript. The output file will be automatically loaded into index.html with the Html Webpack Plugin.

Up and Running

At the end of the day, all you should need to do to start working on your project is type “npm run start” into your command line, point your browser to “localhost:8080”, and open your favorite code editor. Any changes you make are instantly reflected in your browser. When you’re done, enter “npm run build” into your terminal. Your app will be bundled and exported to the “dist” folder. This workflow is a breath of fresh air to anyone who is used to installing dozens of dependency tools before ever writing a line of code. We offload all of this work to NPM. If you notice, even webpack and webpack-dev-server are included in our package.json file, and aliased in the ‘scripts’ property.

“scripts”: {

“start”: “./node_modules/.bin/webpack-dev-server –colors –progress –inline”,

“build”: “./node_modules/.bin/webpack –progress –colors”

},

Tools for Hackers

Webpack is extremely customizable. With just a few lines of code, you can integrate almost any existing library or build tool in the NodeJS ecosystem (that is to say, thousands). In the example, you’ll notice that I’ve included just a few.. I’ve integrated the Eslint JavaScript syntax checker in a pre-loader. Eslint for catching errors and ensuring best practice before ever running your project. Preloaders like this one are configured just like normal loaders, but run before any of the other loaders, so that you get instant feedback if something has gone wrong.

preLoaders: [

{

test: /.jsx?$/,

loaders: [‘eslint’],

exclude: /node_modules/

}

]

You can run compatibility checks and automatically shim newer features to maintain browser compatibility. One such compatibility tool is autoprefixer, an extension of the postcss framework. Postcss is great for automatically mutating and improving styles at compile time. The autoprefixer extension automatically adds vendor prefixes to non-standard css attributes, ensuring compatibility back to a specific version of any browser (or all of them as in the example).

postcss: [

autoprefixer({ browsers: [‘last 3 versions’] })

]

You can even proxy external APIs through your localhost, allowing you to start developing immediately, without having to install any server related dependencies on your local machine. Just add a few lines of configuration to your ‘devServer’ configuration parameter.

devServer: {

historyApiFallback: true,

contentBase: ‘./public’,

proxy: {

// Proxy the url /api to an external API. This way you don’t have to install the server on your computer and can get coding faster.

‘/api’: {

target: ‘https://your-api.com’,

xfwd: true,

changeOrigin: true

}

}

}

Static Outputs – Simplified and Speedy

You’ll notice that the bundled app doesn’t require a server; it’s static HTML, JavaScript, and CSS. This could greatly reduce the complexity of your server environment, and gives you many more options when organizing your development workflow. For example, instead of requiring a server to template out your app whenever a user visits it at runtime, you could template it out at compile time with Webpack, and host it on a static CDN. Any database-driven content (such as comments) could be loaded in with AJAX. This specific example is a very basic one, but is just one of many possibilities involving Webpack that could significantly reduce the energy footprint of your app, saving you a lot of money and headaches over time.

In the example code, you’ll notice that there’s a separate configuration called ‘buildConfig’ in the webpack.config.js file. This is where you can tweak your build-specific settings. At the bottom of the file, we simply check the context in which webpack is being run, and select the appropriate configuration.

switch (lifecycleEvent) {

case ‘build’:

module.exports = buildConfig;

break;

default:

module.exports = devConfig;

break;

}

Conclusion

If you are interested in reducing the server footprint of your app, or have questions about Webpack, please drop us a note. We’d love to hear from you!

More reading about Webpack: