Skip to main content

Bundle your Front-End with Webpack

Peter Cho | Senior Developer

June 13, 2016


In an age where mobile devices are prevalent but public wifi isn’t robust enough to be reliable, performance is important. Despite faster connections, developers still must focus on designing their systems to be as optimal as possible to ensure quick and reliable delivery to their consumers.

Luckily, we have webpack to aide us with this problem. Though daunting and sometimes confusing, when harnessed correctly, you can make it do some really awesome tasks to your static assets, like bundling javascript, css, html, and pngs in one file (yes, one file!) and optimizing them by minification.

screenshot of how webpack module bundler works

The Basics

If you have never used webpack or have tried and failed in the past, read on. This tutorial intends to answer some implementation questions on some of the most common uses of webpack that makes it amazing.

Before you do anything, make sure you have webpack installed:

npm install --save-dev webpack

With that out of the way, take a very basic webpack.config.js file:

'use strict';

const webpack = require('webpack'),

         path = require('path');

let config = {

  entry: myPage: './myPage.js’,

  output: {

    path: './dist',

    filename: 'bundle.js'

  },

};

module.exports = config;

It’s pretty easy to understand: you pass in the files you want webpack to process within entry and the processed outcome gets thrown into a directory called “dist” as “bundle.js.”  Every time you run “webpack” in the command line, a new file will show up there.

You’ve taken your first step at creating a workflow for your static front-end assets. However, it doesn’t really do enough to warrant its value…yet.

Multiple File Entries

What if you have multiple files?  Since the parameters to pass into your webpack configuration is a JSON file, it’s as easy as adding another entry to the entry object.

'use strict';

const webpack = require('webpack'),

         path = require('path');

let config = {

  entry: {

    myPage: './myPage.js',

    myPage2: './myPage2.js',

    myPage3: './myPage3.js'

  },

  output: {

    path: './dist',

    filename: 'bundle--[name].js'

  },

};

module.exports = config;

Notice how output has [name] added to the filename. This allows webpack to map individual bundles to their respective keys in the entry (in this case, we will have “bundle–myPage.js”, “bundle–myPage2.js”, and “bundle–myPage3.js” in our /dist directory).

Combining Assets into Single Bundles

What if you want to jam ten files into one easy-to-serve bundle.  This especially becomes useful if your project demands you to  eliminate round trips to improve browser performance, especially in old Internet Explorer browsers.

You can do this simply by combining entries into a single entry key.

'use strict';

const webpack = require('webpack');

let config = {

  entry: {

    myPages: [

      myPage: './myPage.js',

      myPage2: './myPage2.js',

      myPage3: './myPage3.js'

    ]

  },

...

  output: {

    path: './dist',

    filename: 'bundle--[name].js'

  },

};

module.exports = config;

The above configuration will generate a single bundle called “bundle–myPages.js” including assets from the three files in the array. Useful if you want to organize your stuff in logical bundles.

Poll All Static Assets in a Directory

It may be tedious to add a file to the entry object everytime you create a new file.  Not only does it add an extra step for the developer in an unfamiliar framework, it also could add a lot of technical debt due to lack of knowledge.

Gladly, webpack.config.js is a javascript file that nodejs can interpret, so you have plain javascript and the npm ecosystem to simplify your workflow.  In my case, I prefer to use the  glob module to seek through a directory and add all the files for me.  To do this, first install:

npm install --save glob

..and add it to your entry array like this:

const webpack = require('webpack'),

         glob = require('glob');

let config = {

  entry: {

    // You don't need the following anymore...

    // myPages: [

    //  myPage: './myPage.js',

    //  myPage2: './myPage2.js',

    //  myPage3: './myPage3.js'

    //],

 

    // Auto-detect all pages in directory.

    'myPages': glob.sync('./path/to/**/*.js'),

  },

...

Webpack should now pick up all files in the directory you want it to poll and drop the code into bundle–myPages.js.  Of course, take care what logic you incorporate in your config file so you don’t pick up changes to the wrong files.

Loaders Galore!

By default, webpack assumes all your files are javascript, but one of the big benefits of webpack is that it can load in static non-javascript assets into the same bundle, as well. To do this, our configurations must know how to handle each type, including CSS, HTML, PNG, and SVG.

Identifying them is as easy as specifying loaders, which consists of a regular expression webpack needs to search for and the module you want it to use once it identifies the assets.

'use strict';

const webpack = require('webpack'),

         glob = require('glob');

let config = {

  entry: {

    // Auto-detect all pages in directory.

    'myPages': glob.sync('./path/to/**/*.js'),

  },

  module: {

     loaders: [

       // Javascript: js, jsx

       {

         test: /\.jsx?$/,

         loader: 'babel-loader'

       },

       // CSS: scss, css

       {

         test: /\.s?css$/,

         loaders: ['style', 'css', 'sass', 'postcss-loader']

       },

       // SVGs: svg, svg?something

       {

         test: /\.svg(\?.*$|$)/,

         loader: 'file-loader?name=/img/[name].[ext]'

       },

       // Images: png, gif, jpg, jpeg

       {

         test: /\.(png|gif|jpe?g)$/,

         loader: 'file?name=/img/[name].[ext]'

       },

       // HTML: htm, html

       {

         test: /\.html?$/,

         loader: "file?name=[name].[ext]"

       },

       // Font files: eot, ttf, woff, woff2

       {

         test: /\.(eot|ttf|woff2?)(\?.*$|$)/,

         loader: 'file?name=/fonts/[name].[ext]'

       }

    ]

  },

  output: {

    path: './dist',

    filename: 'bundle--[name].js'

  },

};

 

module.exports = config;

Note: You may have to install several more node modules to make the above configuration work. For example, babel-loader is needed to process javascript files (npm install –save babel-loader). The above displays a few variations of how you can specify the loaders you want to use.

Splitting out vendor assets

Third party packages can often be very big and bulky. Your javascript files may look something like this:

‘use strict’;

let jquery = require(‘jquery’);

let react = require(‘react’);

...

Webpack is intelligent enough to recognize and include these libraries as part of the bundle. However, if you have several pages with similar patterns, webpack will compile all the third party libraries into EACH bundle! This does not bode well because it makes static assets harder to cache and bundles unnecessarily large.

Ideally, we want to bundle the unchanging vendor files into its own bundle and have your custom code reference them, provided you include the files in your HTML code in order. Unfortunately, you cannot simply make modifications to the entry like above. You will also need to tap into the CommonsChunkPlugin plugin to parse the bundled assets in such a way where the namespacing of the included assets are consistent across all files.

So try this:

'use strict';

const webpack = require('webpack'),

         glob = require('glob');

let config = {

  entry: {

    'vendor': [

      'react',

      'angular',

      'jquery'

    ],

 

    // Auto-detect all pages in directory.

    'myPages': glob.sync('./path/to/**/*.js'),

  },

...

  plugins: [

    // Pro-tip: Order matters here.

    new webpack.optimize.CommonsChunkPlugin([‘myPages’, 'vendor'], 'bundle--[name].js')

  ]

};

 

module.exports = config;

Now you have consistent namespacing, and you can include your assets on your static HTML like so:

...

<script src=”bundle--vendor.js”></script>

<script src=”bundle--myPages.js”></script>

...

 

Compress and Minify ALL THE THINGS

To make your assets production ready without too much effort, you can tap into the ugilify plugin to remove all white spaces and minify your assets:

plugins: [

  // Minify assets.

  new webpack.optimize.UglifyJsPlugin({

    compress: {

      warnings: false // https://github.com/webpack/webpack/issues/1496

    }

  })

]

...

 

Ready to Go?

This article touches the surfaces of some of the things webpack can tackle. At first, you’ll notice that you will do a lot of initial configuration work when you first engage with webpack. Once you get it to a point it does everything you need it, it’s a feature that you pretty much set it and forget it (at least from my experiences).  

Complete Configuration

'use strict';

const webpack = require('webpack'),

   glob = require('glob');

let config = {

  entry: {

    'vendor': [

      'react',

      'angular',

      'jquery'

    ],

 

    // Auto-detect all pages in directory.

    'myPages': glob.sync('./path/to/**/*.js'),

  },

  module: {

    loaders: [

      // Javascript: js, jsx

      {

        test: /\.jsx?$/,

        loader: 'babel-loader'

      },

      // CSS: scss, css

      {

        test: /\.s?css$/,

        loaders: ['style', 'css', 'sass', 'postcss-loader']

      },

      // SVGs: svg, svg?something

      {

        test: /\.svg(\?.*$|$)/,

        loader: 'file-loader?name=/img/[name].[ext]'

      },

      // Images: png, gif, jpg, jpeg

      {

        test: /\.(png|gif|jpe?g)$/,

        loader: 'file?name=/img/[name].[ext]'

      },

      // HTML: htm, html

      {

        test: /\.html?$/,

        loader: "file?name=[name].[ext]"

      },

      // Font files: eot, ttf, woff, woff2

      {

        test: /\.(eot|ttf|woff2?)(\?.*$|$)/,

        loader: 'file?name=/fonts/[name].[ext]'

      }

    ]

  },

  output: {

    path: './dist',

    filename: 'bundle--[name].js'

  },

  plugins: [

    // Pro-tip: Order matters here.

    new webpack.optimize.CommonsChunkPlugin([‘myPages’, 'vendor'], 'bundle--[name].js'),

    // Minify assets.

    new webpack.optimize.UglifyJsPlugin({

      compress: {

        warnings: false // https://github.com/webpack/webpack/issues/1496

      }

    })

  ]

};

module.exports = config;


Recommended Next
DevOps
Common Configuration Changes for Your Outrigger-based Project
Black pixels on a grey background
DevOps
Adding MySQL Slow Query Logs to Logstash
Black pixels on a grey background
DevOps
Static Drupal - Taking the pain out of Drupal hosting
Black pixels on a grey background
Jump back to top

深圳SEO优化公司乐山网站seo优化报价木棉湾网站搭建公司坪地网站推广哪家好塔城百度标王石岩模板网站建设公司池州营销型网站建设蚌埠百度竞价报价庆阳关键词按天收费多少钱大庆SEO按天收费价格丹竹头网站推广哪家好黄山百度竞价多少钱河源百度关键词包年推广价格同乐关键词按天计费哪家好巢湖网站设计报价无锡网站推广方案公司厦门外贸网站建设公司揭阳网站改版公司湖州百姓网标王报价昭通企业网站设计报价邢台关键词排名塘坑网站改版推荐哈密网络广告推广价格大浪英文网站建设哪家好清徐网站推广工具报价南京网站优化排名公司临沧关键词按天扣费推荐漯河模板推广商洛网站推广方案价格大芬营销型网站建设多少钱横岗网站制作价格歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化