Christopher Bloom, @illepic

cbloom@phase2technology.com

 

Follow along: github.com/illepic/webpack-intro

Every project we build must juggle dozens, hundreds, even thousands of frontend assets, combined in different ways and dependent on each other in complex relationships.

Webpack brings sanity to managing (and splitting and loading) frontend assets.

Precompiled

  • Coffeescript, JSX, ES6/7
  • Mustache, Twig, Handlebars
  • SCSS, Less, CSS
  • Source images
  • Font icons

Compiled

  • Javascript
  • HTML
  • CSS
  • Optimized PNG, JPG, SVG
  • Fonts

Then we transform

  • Auto browser support
    • ES6 -> ES5, Browser prefixes
  • Concatenate
    • Combine a lot of files into one file
  • Uglify
    • This code uses a lot of long words, let's make them all short words.
  • Inline
    • Do we need another http request to this image? Why not just base64 it right here?
  • CDN-ify
    • Some of this js can be served from a CDN for better caching.

Why now?

 

Today’s websites are evolving into web apps:

 

More and more JavaScript is in a page.
You can do more stuff in modern browsers.
Fewer full page reloads → even more code in a page.
As a result there is a lot of code on the client side!

A big code base needs to be organized. Module systems offer the option to split your code base into modules.

 

https://webpack.js.org/concepts/why-webpack/

Why now for us?

 

Many of the things we build are actually SPAs where a content management system (Drupal) acts as our asset pipeline, minifier, and initial loader. Like:

 

 

Juggling when/how these assets load can be tricky.

The good news is that we've naturally built Webpack already kinda!

Using Grunt/Gulp

A little differently.

On every project.

Grunt/Gulp

  1. I'll install this library with Bower
  2. But I really only need it on this landing page
  3. Meh, let's just bundle it all together
  4. Oh, I have to tell Grunt where to find it so we can concat/uglify/move it.
  5. Better edit that Gruntfile.js. Which tasks should I add it to again?

What if...

smart-slider.js

import sliderLibrary from 'big-ass-slider';
import {adclicks} from '../components/analytics-library-we-wrote';

require('./slider-custom-styles.scss');

export default class slider {
    return sliderLibrary.render().onClick( () => {adclicks.fire();});
}
import $ from 'jquery';
import ourSlider from '../components/smart-slider';

$('#should-i-use-a-slider').html(ourSlider());

elegant-product-page.js

/* Minified jQuery library in ES5 */
/* Minified slider library in ES5 */
/* Minified adclick library in ES5 */

$('#should-i-use-a-slider').html(ourSlider());

Resulting bundle.js

/* Minified Bootstrap css */

.custom-slider { background: blue; }
.custom-slider--adclicks { color: red; }

Resulting bundle.css

What just happened?

  1. ES6 syntax
  2. ES6 module style
  3. Easy importing of node modules
  4. No worries about load order
  5. Wait. Sass in javascript? Wat.

Grunt/Gulp are still important, but Webpack unifies and standardizes the approach to frontend dependency management and building.

Fire it up

npm init
npm install webpack webpack-cli --save-dev
touch index.js

Create webpack.config.js

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

Run it

A

What happened?

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

Starting point to figure out how to bundle

The resulting bundle of all our code

"Where we are right now"

We haven't written any js yet, why do we now have a bundle.js file?

Out of the box, Webpack only understands javascript.

Let's write js the webpack way

1. Toss in an index.html that references bundle.js

<!DOCTYPE html>
<html lang="en">
    <head></head>
    <body>
        <img id="doit" src="https://i.imgur.com/2EXEB4s.png"/>
        <script src="bundle.js"></script>
    </body>
</html>

2. Let's install jQuery because why not

npm install jquery --save

3. Write js that looks familiar: index.js

var $ = require('jquery');
$(document).ready(function() {
  $('#doit').click(function() {
    alert("DON'T LET YOUR DREAMS BE DREAMS");
  });
});

4. Now webpack it

B

Make our own modules

1. Write ES6 module syntax: components/doit.js

2. Require and use in index.js

  • ES6 -> ES5 Babel transpiling!
  • No script tag shuffling
  • No Gruntfile editing
  • No f-it-we'll-do-it-live folder globbing

C

CSS in ... javascript? Webpack loaders

1. Install "style loaders"

npm install style-loader css-loader --save-dev

2. Add the "loaders" for css to webpack.config.js.

Processes from bottom to top:

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        { loader: 'style-loader' },
        { loader: 'css-loader' }
      ]
    },
  ]
}
  • Fully modular frontend components
  • But, literally "css in js"
import $ from 'jquery';
import './doit.css';

export default {
  labeoufFlex() {
    $(document).ready(() => {
// ...

3. Require css files from within js: 

components/doit.js.

D

4. Run `webpack`

Sass in ... javascript?

1. Install sass-loader

npm install node-sass sass-loader --save-dev

2. Add the sass-loader to webpack.config.js

  // ...
  {
    test: /\.scss$/,
    use: [
      { loader: 'style-loader' },
      { loader: 'css-loader' },
      { loader: 'sass-loader' },
    ]
  },
  • See the loader pattern?
  • Pipe transformations from one loader to the next
import $ from 'jquery';
import './doit.scss';

export default {
  labeoufFlex() {
    $(document).ready(() => {
//...

3. Require scss files from within js, run `webpack`

E

Injecting CSS into <head> seems like cheating.

Webpack does generate standalone CSS files with the extract-text-webpack plugin

Dev server?

1. Install webpack-dev-server

npm install webpack-dev-server --save-dev

2. We'll run via npm, add to package.json:

// ...
"scripts": {
 ...
  "wds": "webpack-dev-server"
},
// ...
devServer: {
  historyApiFallback: true,
  hot: true,
  inline: true,
  progress: true,
  contentBase: path.resolve(__dirname),
},
plugins: [
  new webpack.NamedModulesPlugin(),
  new webpack.HotModuleReplacementPlugin(),
]
// ...

3. Update webpack.config.js

4. Run `npm wds` 

5. Visit localhost:8080/webpack-dev-server/

6. Change files

F

Multiple entry points (and "chunking")

module.exports = {
  entry: {
    pageA: path.resolve(__dirname, 'pageA'),
    pageB: path.resolve(__dirname, 'pageB'),
  },
  output: {
    path: path.join(__dirname, "js"),
    filename: "[name].bundle.js",
    chunkFilename: "[id].chunk.js"
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'commons',
      minChunks: 2,
    }),
  ]
};

1. Update webpack.config.js

2. See file structure here: 

https://github.com/illepic/webpack-intro/tree/master/pres/G

F

Things we didn't cover

  • PLUGINZ!!

  • Parsing/minifying fonts/images

  • Proxying to a running local Drupal site so all assets are hot-provided

http://bensmithett.com/smarter-css-builds-with-webpack/

Thank you

Webpack

By Christopher Bloom

Webpack

Webpack is a tool to help you build bundles of assets for your applications.

  • 3,435