name: cover
# Managing your front-end with webpack Johannes Ewald
MNUG / July 9th, 2014 --- layout: true class: center, middle .slide-header-left[ Managing your front-end with webpack ] .slide-header-right[ MNUG / July 9th, 2014 ] --- ## About me My name is Johannes Ewald ---
---
[webpack.github.io](http://webpack.github.io/) --- What is a module bundler? --- `a.js` ```javascript var b = require("./b.js"); console.log(b); ``` `b.js` ```javascript module.exports = 42; ``` --- `bundle.js` ```javascript ({ "./a.js": function(module, exports, require) { var b = require("./b.js"); console.log(b); }, "./b.js": function(module, exports, require) { module.exports = 42; }}) ``` --- `bundle.js` can now be executed anywhere. --- ```html
Example
```
--- So how does webpack work? --- Via command line ```bash $ webpack
``` ```bash $ webpack a.js bundle.js ``` ```bash Hash: a771515e38c2f7407e3b Version: webpack 1.3.1-beta7 Time: 77ms Asset Size Chunks Chunk Names bundle.js 1682 0 [emitted] main [0] ./a.js 45 {0} [built] [1] ./b.js 22 {0} [built] ``` --- Via config ```bash $ webpack --config
``` ```javascript module.exports = { entry: path.resolve(__dirname, "./a.js"), output: { path: __dirname, filename: "bundle.js" } }; ``` -- If your config is called `webpack.config.js` you can just run ```bash $ webpack ``` --- `bundle.js` .code[ ```javascript /******/ (function(modules) { // webpackBootstrap /******/ /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { var b = __webpack_require__(1); console.log(b); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { module.exports = 42; /***/ } /******/ ]) ``` ] --- - A small module system is included at the beginning - Filenames are transformed to ids (in contrast to browserify) --- But what about different module styles? --- CommonJS ```javascript var someModule = require("./someModule.js"); module.exports = 42; ``` AMD ```javascript define(["./someModule.js"] , function (someModule) { return 42; }); ``` ES6 modules
(using es6-loader)
```javascript import someModule from "./someModule.js"; export default 42; ``` --- But websites are more than just JavaScript: - HTML - CSS - Fonts - Images --- Wouldn't it be nice if we could all just **require()™** them? --- `HelloWorld.js` ```javascript var HelloWorld = View.extend({ template: require("./HelloWorld.html"), styles: require("./HelloWorld.css") }); ``` --- `HelloWorld.html` ```html
Hello World
``` `HelloWorld.css` ```css .hello-world h1 { color: hotpink; } ``` --- But webpack doesn't understand HTML or CSS, it's a module bundler for JavaScript. --- That's were loaders come in... --- ## Loaders > Loaders are transformations that are applied on files. They preprocess files. For instance they can transform CoffeeScript to JavaScript.
[webpack.github.io/docs/loaders.html](http://webpack.github.io/docs/loaders.html)
--- ## raw-loader Turns ... ```html
Hello World
``` ... into ... ```javascript module.exports = "
\n
Hello world
\n
"; ``` --- Or ... ```css .hello-world h1 { color: hotpink; } ``` ... into ... ```javascript module.exports = ".hello-world h1 {\n color: hotpink;\n}"; ``` --- How are loaders applied? --- First of all we need to install the loaders we want to use ```bash $ npm i raw-loader --save ``` --- Inlined ```javascript var HelloWorld = View.extend({ template: require("raw!./HelloWorld.html"), styles: require("raw!./HelloWorld.css") }); ```
(as proposed by CommonJS)
--- Via config ```javascript module.exports = { // ... module: { loaders: [ { test: /\.html$|\.css$/i, loader: "raw" } ] } // ... }; ``` -- ```javascript var HelloWorld = View.extend({ template: require("./HelloWorld.html"), styles: require("./HelloWorld.css") }); ``` --- - HTML ✔ - CSS ✔ - Fonts - Images --- CSS provides a syntax for referencing required files: ```css @font-face { font-family: 'Yanone Kaffeesatz'; src: url(assets/fonts/yanonekaffeesatz-regular-webfont.woff) format('opentype'); font-weight: normal; font-style: normal; } ``` ```css @import "reset.css"; ``` --- So does HTML: ```html
``` --- Wouldn't it be nice if webpack just understood HTML and CSS? --- ```bash $ npm i html-loader css-loader --save ``` --- ```javascript module.exports = { // ... module: { loaders: [ { test: /\.html$/i, loader: "html" }, { test: /\.css$/i, loader: "css" } ] } // ... }; ``` --- ```bash $ webpack Hash: 1fe807fbecf26b07a8f8 Version: webpack 1.3.1-beta7 Time: 251ms Asset Size Chunks Chunk Names bundle.js 2873 0 [emitted] main [0] ./main.js 46 {0} [optional] [built] [1] ./HelloWorld.js 159 {0} [optional] [built] [2] ./View.js 157 {0} [optional] [built] [3] ./HelloWorld.html 158 {0} [optional] [built] [1 error] [4] ./HelloWorld.css 287 {0} [optional] [built] [1 error] [5] ./assets/img/alamid-logo.svg -1 [optional] [built] [failed] [6] ./assets/fonts/yanonekaffeesatz-regular-webfont.woff -1 [optional] [built] [failed] ERROR in ./assets/img/alamid-logo.svg Module parse failed: /home/jhnns/example/assets/img/alamid-logo.svg Line 1: Unexpected token < You may need an appropriate loader to handle this file type. | | | @ ./HelloWorld.html 1:96-135 ERROR in ./assets/fonts/yanonekaffeesatz-regular-webfont.woff Module parse failed: /home/jhnns/example/assets/fonts/yanonekaffeesatz-regular-webfont.woff Line 1: Unexpected token ILLEGAL You may need an appropriate loader to handle this file type. (Source code omitted for this binary file) @ ./HelloWorld.css 2:74-128 ``` --- How to handle assets that don't need to be interpreted further? --- ```bash $ npm i file-loader --save ``` --- ```javascript module.exports = { // ... module: { loaders: [ { test: /\.html$/i, loader: "html" }, { test: /\.css$/i, loader: "css" }, { test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$|\.wav$|\.mp3$/, loader: "file" } ] } // ... }; ``` --- ```bash $ webpack Hash: e33cfe8969598388e39c Version: webpack 1.3.1-beta7 Time: 280ms Asset Size Chunks Chunk Names c83e8869cc75b1463cb291373a051753.woff 45752 [emitted] 8f73676dc795be128a9bc94a2695e3be.svg 8707 [emitted] bundle.js 2867 0 [emitted] main [0] ./main.js 46 {0} [optional] [built] [1] ./HelloWorld.js 159 {0} [optional] [built] [2] ./View.js 157 {0} [optional] [built] [3] ./HelloWorld.html 158 {0} [optional] [built] [4] ./HelloWorld.css 296 {0} [optional] [built] [5] ./assets/fonts/yanonekaffeesatz-regular-webfont.woff 82 {0} [optional] [built] [6] ./assets/img/alamid-logo.svg 81 {0} [optional] [built] ``` --- What does the bundle look like now? ---
--- .code[ ```javascript /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { var HelloWorld = __webpack_require__(1); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { var View = __webpack_require__(2); module.exports = View.extend({ template: __webpack_require__(3), styles: __webpack_require__(4) }); /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { module.exports = { extend: function (view) { return function (parent) { parent.innerHTML = view.template; }; } }; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { module.exports = "
\r\n
Hello World
\r\n
\r\n
\r\n"; /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { module.exports = "@font-face {\r\n font-family: 'Yanone Kaffeesatz';\r\n src: url("+__webpack_require__(5)+") format('opentype');\r\n font-weight: normal;\r\n font-style: normal;\r\n}\r\n\r\n.hello-world h1 {\r\n color: hotpink;\r\n}\r\n"; /***/ }, /* 5 */ /***/ function(module, exports, __webpack_require__) { module.exports = __webpack_require__.p + "c83e8869cc75b1463cb291373a051753.woff" /***/ }, /* 6 */ /***/ function(module, exports, __webpack_require__) { module.exports = __webpack_require__.p + "8f73676dc795be128a9bc94a2695e3be.svg" /***/ } /******/ ]) ``` ] --- But what if we wanted to use LESS instead of CSS? --- ```css @font-face { font-family: 'Yanone Kaffeesatz'; src: url(assets/fonts/yanonekaffeesatz-regular-webfont.woff) format('opentype'); font-weight: normal; font-style: normal; } @color: hotpink; .hello-world { h1 { color: @color; } } ``` --- ```bash $ npm i less-loader --save ``` -- There's a loader for everything™ --- ```javascript module.exports = { // ... module: { loaders: [ // ... { test: /\.less$/i, loader: "less" }, // ... ] } // ... }; ``` **?** --- ```bash $ webpack Hash: 1bd24bdbc06f3ffd597d Version: webpack 1.3.1-beta7 Time: 209ms Asset Size Chunks Chunk Names 8f73676dc795be128a9bc94a2695e3be.svg 8707 [emitted] bundle.js 2528 0 [emitted] main [0] ./main.js 46 {0} [optional] [built] [1] ./HelloWorld.js 160 {0} [optional] [built] [1 error] [2] ./View.js 157 {0} [optional] [built] [3] ./HelloWorld.less -1 [optional] [built] [failed] [4] ./HelloWorld.html 158 {0} [optional] [built] [5] ./assets/img/alamid-logo.svg 81 {0} [optional] [built] ERROR in ./HelloWorld.less Module parse failed: /home/jhnns/example/HelloWorld.less Line 1: Unexpected token ILLEGAL You may need an appropriate loader to handle this file type. | @font-face { | font-family: 'Yanone Kaffeesatz'; | src: url(assets/fonts/yanonekaffeesatz-regular-webfont.woff) format('opentype'); @ ./HelloWorld.js 5:12-40 ``` --- That's because the LESS-loader generates CSS. -- We need to chain both loaders. --- ```javascript module.exports = { // ... module: { loaders: [ // ... { test: /\.less$/i, loader: "css!less" }, // ... ] } // ... }; ``` or inline ```javascript require("css!less!./HelloWorld.less"); ``` --- And talking about chaining loaders ... ... you should always chain `style!css` --- .code[ ```javascript /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { var HelloWorld = __webpack_require__(1); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { var View = __webpack_require__(2); module.exports = View.extend({ template: __webpack_require__(3), styles: __webpack_require__(4) }); /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { module.exports = { extend: function (view) { return function (parent) { parent.innerHTML = view.template; }; } }; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { module.exports = "
\r\n
Hello World
\r\n
\r\n
\r\n"; /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { // style-loader: Adds some css to the DOM by adding a