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