Components

&

module bundlers

Front-end Modern Practices

part 2

Components

noun

a constituent part; element; ingredient.

 

adjective

being or serving as an element (in something larger)

Components

A

A

A

B

C

<page>
  <group>
    <picture></picture>
    <picture></picture>
  </group>
  <picture></picture>
</page>

Components

CSS withBEM

.block {}

.block__element {}

.block__element--modifier {}
block == component*

* well, not always!

Components

JavaSCRIPT Modules

// utility.js
function generateRandom() {
    return Math.random();
}

function sum(a, b) {
    return a + b;
}

export { generateRandom, sum }
// component.js
import { generateRandom, sum } from 'utility';

console.log(generateRandom()); //logs a random number
console.log(sum(1, 2)); //3

Components

JavaSCRIPT Modules

// utility.js
var utils = {
  generateRandom: function() {
    return Math.random();    
  },
  sum: function(a, b) {
    return a + b;
  }
};

export default utils;
// component.js
import utils from 'utility';

console.log(utils.generateRandom()); //logs a random number
console.log(utils.sum(1, 2)); //3

Components

COMMON.JS

// utility.js
var utils = {
  generateRandom: function() {
    return Math.random();    
  },
  sum: function(a, b) {
    return a + b;
  }
};

module.exports utils;
// component.js
var utils = require('utility');

console.log(utils.generateRandom()); //logs a random number
console.log(utils.sum(1, 2)); //3

WEBPACK

webpack takes modules with dependencies and generates static assets representing those modules.

WEBPACK

WEBPACK PHILOSOPHY

Everything is a module

Code spliting into chunks

Not just Javascript can become a module. You can require HTML, CSS and even Images.

require('myComponent.js')
require('myStyles.css')

All assets are split into smaller parts that can be loaded asynchronously

WEBPACK Loaders

Loaders

Similar to tasks, loaders allow you to preprocess files as you require() or “load” them.

 

Loaders can transform files from a different language like, CoffeeScript to JavaScript, or inline images as data URLs.

 

Loaders even allow you to do things like require() css files right in your JavaScript!

WEBPACK Loaders

Loaders

{
    module: {
        loaders: [
            { test: /\.coffee$/, loader: "coffee-loader" }
        ],
        preLoaders: [
            { test: /\.coffee$/, loader: "coffee-hint-loader" }
        ]
    }
};

WEBPACK Loaders

module: {
    loaders: [
      { test: /\.coffee$/, loader: 'coffee-loader' },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  }

query – Configuration object for loader

Chaining Loaders

module: {
  loaders: [
    // use ! to chain loaders
    { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, 
    { test: /\.css$/, loader: 'style-loader!css-loader' },
    // inline base64 URLs for <=8k images, direct URLs for the rest
    { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } 
  ]
}

The data flows from right to left. In the above example:

 

1. Less-loader – transforms .less files into .css files

2. Css-loader – injects css code inside the .js file

3. Style-loader – injects the css code from .js file into the page inside <style></style> tags.

WEBPACK Config

Basics

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

Starting webpack

$ webpack // for building once for development
$ webpack -p // for building once for production (minification)
$ webpack --watch // for continuous incremental build in development (fast!)
$ webpack -d // to include source maps

WEBPACK Config

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'       
  },
  module: {
    loaders: [
      { test: /\.coffee$/, loader: 'coffee-loader' },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  },
  resolve: {
    // you can now require('file') instead of require('file.coffee')
    extensions: ['', '.js', '.json', '.coffee'] 
  }
};

Multiple entryPoints

// webpack.config.js
module.exports = {
  entry: {
    Profile: './profile.js',
    Feed: './feed.js'
  },
  output: {
    path: 'build',
    filename: '[name].js' // Template based on keys in entry above
  }
};

This will generate 2 bundles:

Profile.js and Feed.js

WEBPACK Code SPLITING

Webpack has two types of dependencies in its dependency tree: sync and async.

Async dependencies act as split points and form a new chunk. After the chunk tree is optimized, a file is emitted for each chunk.

// cats.js
var cats = ['dave', 'henry', 'martha'];
module.exports = cats;

// app.js
var cats = require('./cats.js');
console.log(cats); // ['dave', 'henry', 'martha']

// or asynchronously
require.ensure(['cats.js'], function (require) {
    var cats = require('cats.js');
});

WEBPACK Code Spliting

Example

// example.js
var a = require("a");
var b = require("b");
require.ensure(["c"], function(require) {
    require("b").xyz();
    var d = require("d");
});

OUTPUT

// output.js
webpack module loading code

module a
module b

webpack async module require
// 1.js
webpackJsonp([1],[
  module c
  module d
]);

WEBPACK Code Spliting

Example usage with Routing

if (window.location.pathname === '/feed') {
  showLoadingState();
  // this syntax is weird but it works
  require.ensure([], function() {
    hideLoadingState();
    // when this function is called, 
    // the module is guaranteed to be synchronously available.
    require('./feed').show();
  });
} else if (window.location.pathname === '/profile') {
  showLoadingState();
  require.ensure([], function() {
    hideLoadingState();
    require('./profile').show();
  });
}

Images and Styles 

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    // This is where images AND js will go
    path: './build', 
    // This is used to generate URLs to e.g. images
    publicPath: 'http://mycdn.com/', 
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' },
      { test: /\.css$/, loader: 'style-loader!css-loader' },

      // inline base64 URLs for <=8kB images, direct URLs for the rest
      { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } 
    ]
  }
};

Images and Styles 

require('./bootstrap.css');
require('./myapp.less');

// picture.png is larger than 8kB
var img = document.createElement('img');
img.src = require('./picture.png'); 

// logo.png is smaller than 8kB
var logo = document.createElement('img');
logo.src = require('./logo.png');

Will OUTPUT

Both bootstrap.css and myapp.less (as css) will be injected into the page inside <style></style> tags.

picture.png – url will equal "http://mycdn.com/picture.png"

logo.png – will be injected as base64 encoded image

BONUS

Hot Module Replacement

HOT HOT HOT!

Add, remove and modify modules „on the fly” without refreshing the page.

 

This means the state is preserved. For example, if you fill a form it won’t get lost.

 

Can swap javascript, css, images, templates.

BONUS

Fancy Loaders

HOT HOT HOT!

Like vue-loader for Vue.js, which gives you the possibility to have JavaScript, HTML and CSS (with preprocessors for all of them) inside a .vue component file!

Thanks for listening!

With         from Monterail

Damian Dulisz

Register at: monterail.com/pwr

Components & Webpack

By Damian Dulisz

Components & Webpack

  • 1,743