27 March 2016

webpack part I

other solutions

For my part I was already familiar with grunt/gulp and had just heard about webpack as this thing more and more people started to use. First thing I heard about webpack was that it was configuration heavy and I thought didn’t we just leave grunt for gulp because of it?

Having tried it out it feels pretty smooth. This post will cover the very basics of web pack which is installation, using it for command line, es6, source map and some production preparation. How you enjoy it..

The next part will cover more real world scenarios.

server side tools

asp .net rails, built bundling solutions

task runners

A task runner usually has the job of concatenating a bunch of files to one or a few files and compress them in the process. Grunt does this for us. Gulp as well although gulp doesn’t create tmp files in the process but operates on streams.

gulp,grunt

file.es6 => file.js
file.es6 => file.js
file.es6 => file.js file.js  browser
file.es6 => file.js file.js
file.es6 => file.js
file.es6 => file.js

webpack

First thing you need to know with webpack is that it draws all its packages from the NPM, node package manager. It supports commonjs out of the box but you can make it support es2015 using babel. Webpack wants you to usea module system when you split up your project into many files so decide one of these three

  • amd,
  • commonjs
  • ES2016

Install

You can install webpack both globally and locally depending on your needs.

npm install webpack -g

CLI, Command line Usage

Assume we have

index.html
app.js

On entry point run

webpack ./app.js bundle.js

Then ensure your index.html looks like this:

<html>
	<head></head>
    <body>
        Your application
        <script src="bundle.js"></script>
    </body>
</html>	 

Pointing to our newly created bundle.js

Usage with config file

Add a webpack.config.js

Lets do the same thing as we did on the command line.

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

Running

webpack

With no args produces the same result as before

Watch mode

webpack --watch

Any changes will lead to bundle.js being rebuilt

OR we add

watch: true 

To config file

Let’ use a dev-server

npm install webpack-dev-server -g

To run it just type:

webpack-dev-server

hot deploy

It does have hot deploy mode where your changes happens automatically

There are different ways to accomplish this

1) run with the –hot flag

2) install HotModule plugin, and instruct config file to run it instead

–hot flag

webpack-dev-server –hot

webpack-dev-server --hot

Running it with this flag should lead to your bundle being rebuilt and browser to be reloaded. However to see this happen you will also give it the arguments:

localhost:8080/webpack-dev-server/

You can run it with a–simple flag, and access your app like so

localhost:8080/
hotmodule plugin

Change config file to this

var webpack = require("webpack");

module.exports = {
    entry : "./app.js",
    output : {
        path: path.resolve('build/js'),
        publicPath: "/public/js/",
        filename : "bundle.js",
        
    },
    watch: true,
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
}

Adding this:

var webpack = require("webpack");

And this:

plugins: [
        new webpack.HotModuleReplacementPlugin()
]

To make hot deploy work we need to configure our output correctly.

output : {
    path: path.resolve('build/js'),
    publicPath: "/public/js/",
    filename : "bundle.js",
    
}

This means that running

webpack

OR

webpack-dev-server

will mean our bundle.js will be built to path + filename which is

/build/js/bundle.js

BUT to access it in the browser the script tag will need to point to our publicPath

So index.html script-tag will have to look like

<script src="public/js/bundle.js"></script>

Try changing a js-file now and it work like a charm.. :)

working with more than one file

As your app grows you want to split it up into more than one file. Coming from the nodejs world you are used to using require, which uses the commonjs module system. Assume we have the following files:

// test.js
var test = "I contain test";
module.exports = test;	

// app.js
var test = require('./test');

console.log('app loaded- ' + test);    

We are requiring test.js as a dependency. We run

webpack  // rebuild bundle
webpack-dev-server // run our program on :8080

And we see its working, we have a way to work with modules

adding third party libs to our solution

What if we are in a place where we want to add files to our solution but we don’t want to do require on it but should still be part of our app? We can add it by adding to our config file.

We change our config file to this:

module.exports = {
    entry : ['./thirdparty',"./app.js"],
    output : {
        path: path.resolve('build/js'),
        publicPath: "/public/js/",
        filename : "bundle.js",
        
    },
    watch: true,
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
}	

Changing entry row to:

entry : ['./thirdparty',"./app.js"]

I.e we change it into an array and adds an extra file. The extra file is now added to the bundle even though we are not explicitly requiring it in code.

loaders

Lets teach webpack how to do some new things like es2015. For that we will use babel. So we need the following

babel-core
babel-loader
babel-preset-es2015

This will give us babel itself but also all es2015 features that babel knows about which is roughly 70% of the specifications.

So let’t install these:

npm install babel-core babel-loader babel-preset-es2015	--save-dev	

Also we need to add a .babelrc file specifying that we want to transpile from es2015

//.babelrc

{
	"presets" : "es2015"
} 

write your es6 code and consuming it

// test.es6
class Test{
    constructor() {
        this.animal = "monkey"
    }
    
    sayHi () {
        console.log(`hellooo ${this.animal}`);
    }
}

module.exports = Test; 

// consuming it in app.js

var  Test = require('./test')
var t = new Test();
t.sayHi();

adding webpack config

module : {
    loaders : [
        {
            test : /\.es6$/,
            exclude: /node_modules/,
            loader : "babel-loader"
        }
    ]
},
resolve : {
    extensions : [ '','.js','.es6' ]
}

The first part with module::loaders basically says

test, what to look for and process

exclude what not to process

loader what tool to run this

The second part module::resolve basically says what file endings to process when we do require. By adding es6 to this we don’t need to write the full name

require('./file.es6')

it is enough to type

require('./file')

It should be said, we could easily just change

loaders : [
        {
            test : /\.es6$/,

To

loaders : [
        {
            test : /\.js$/,

Its more of a nice thing that we say which files are es6 and which are not.

running the code

webpack-dev-server

another example with es6, using es6 import

//test.js
export class Test{
    constructor() {
        this.animal = "monkey"
    }
    
    sayHi () {
        console.log(`hello ${this.animal}`);
    }
} 

export class Test2{
    test2(){
        console.log("test2");
    }
}

//app.js

import { Test, Test2 } from './test.js';

let t = new Test();
t.sayHi();

let t2= new Test2();
t2.test2();

When we have 2 exports or more we can use

{ Test, Test2 }

When we have only one export like so:

//test.js
export class Test{
    constructor() {
        this.animal = "monkey"
    }
    
    sayHi () {
        console.log(`hello ${this.animal}`);
    }
}

We need to add default keyword to export

//test.js
export default class Test{
    constructor() {
        this.animal = "monkey"
    }
    
    sayHi () {
        console.log(`hello ${this.animal}`);
    }
}

// app.js	
import Test from './test';

OR we do

exports { Test }

source maps

You can’t really be expected to work with a bundle.js file while in dev mode so for that reason we need to add sourcemaps. Sourcemaps saves information of what files your project used to consist of before being bundled. So lets do that:

webpack -d

OR

weback-dev-server -d	

going to production

webpack -p

This minifies our bundle

Hope you had a good read. Stay tuned for part 2.

If you want to try out everything we did in the post have a look at this repo

webpack-starting out



blog comments powered by Disqus