Webpack 2: The Complete Developer's Guide
- because spa has alot of js code and that js code needs to be organized
-
With alot of js modules, two issues arise:
- gauranteeing load order (e.g. if index.js relies on utils.js and store.js, how can we make sure that everytime we run the js code, utils.js and store.js loads first before index.js) (i.e. dependency tree)
- have many js files and loading them over http connection is very slow especially on mobile devices
-
webpack takes big collection of small js modules and putting them inside one bundle.js .. also handles transpiling es6/7 to es5, css, but those are side effects. This guarentees that:
- load order
- have only 1 module
- make 2 js modules and see what webpack does
- what we are gonna do:
- make npm project
- create 2 js modules
- install and config webpack
- run + inspect webpack output
package.json
{
"dependencies": {
}
}// src/index.js
// index.js calls functions in the sum.js
// index imports sum, so sum.js needs to LOAD BEFORE index.js// src/sum.js
// a utility function for math operations
const sum = (a, b) => a + b;
// remember that each js file has it's own scope- CommonJS modules: require, module.exports, sync
- AMD: async module loading
- ES2015: import
- sum.js has no dependencies
- index.js needs sum.js
input: sum.js --> index.js --> webpack--> output: bundle.js
^
|
webpack.config.js
// src/index.js
const sum = require('./sum');
const total = sum(10, 5);
console.log(total);// src/sum.js
const sum = (a, b) => a+b;
module.exports = sum package.json
{
"devDependencies": {
}
}// webpack.config.js
// webpack will look at this file to get the configurations
// the index.js file is the thing that kicks off our app, so we call it the entry point of our appplication
const config = {
// 2 minimum properties we have to config:
// 1. the entry property:
// - the index.js file is the file that we run to start our project
// - the index.js is also the one that export anything (i.e. no other file depends on the index.js), so it is the ENTRY FILE
// - webpack would start at the entry file and look at the files that it imports, then look at the other file that those import and so forth. That's how it builds the dependency tree
entry: './src/index.js'
};
module.exports = config;const path = require('path');
const config = {
// 2 minimum properties we have to config:
// 1. the entry property:
// - the index.js file is the file that we run to start our project
// - the index.js is also the one that export anything (i.e. no other file depends on the index.js), so it is the ENTRY FILE
// - webpack would start at the entry file and look at the files that it imports, then look at the other file that those import and so forth. That's how it builds the dependency tree
entry: './src/index.js',
// 2. output property:
output: {
path: path.resolve(__dirname, 'build'), // HAS TO BE ABSOLUTE PATH. .resolve() creates a path string for any os system
// __dirname, build puts the bundle.js in a folder called 'build' in the homedir of our project
filename: 'bundle.js'
}
};
module.exports = config;package.json
{
"scripts": {
"build": "webpack"
},
"devDependencies": {
"webpack": "^2.2.0-rc.0"
}
}- why would we make a script to run just one command?
- because when we set the command 'webpack' to 'npm run build', it will run the webpack installed inside the project. else it would run the global webpack
- run
npm run build. Notice that the output bundle.js is always much larger than the source files that it built
- this is what the
bundle.jssort of looks like (pseudo code):
var myModules = [
function() { // everything inside of sum.js
const sum = (a,b) => a+b;
return sum;
},
function() { // everything inside of index.js
const sum = myModules[0]();
const total = sum(10, 10);
console.log(total);
}
];
var entryPointIndex = 1;
myModules[entryPointIndex]();to run the bundle.js we will have 1 html file that loads the bundle.js, we will call this file index.html
<!-- index.html -->
<head>
</head>
<body>
<script type="text/javascript" src="bundle.js" />
</body>- webpack determines the dependencies of a bunch of modules and orders them
- module loaders: do some pre-processing before putting modules into
bundle.js - e.g. Babel, images, etc etc
- First module loader we will look at is Babel
- Babel transpiles ES6,7,8 to ES5.
- To be clear:
- Babel: transpiles ES6,7,8 to ES5.
- Webpack: links up js modules together
- there's 3 modules we need to get Babel setup:
- babel-loader: Teaches babel how to work with webpack. Babel can work wth dozen build systems, not just webpack. So babel needs to configured to work with webpack.
- babel-core: knows how to take code, parse it, and generate some output file. Babel doesn't
- babel-preset-env: ruleset for telling babel exactly what pieces of es6/7/8 syntax to look for to turn into es5. E.g. 'look at the const keyword, look at the object preset'
- command:
yarn add --dev babel-loader babel-core babel-preset-env
// webpack.config.js
// webpack will look at this file to get the configurations
// the index.js file is the thing that kicks off our app, so we call it the entry point of our appplication
const config = {
// 2 minimum properties we have to config:
// 1. the entry property:
// - the index.js file is the file that we run to start our project
// - the index.js is also the one that export anything (i.e. no other file depends on the index.js), so it is the ENTRY FILE
// - webpack would start at the entry file and look at the files that it imports, then look at the other file that those import and so forth. That's how it builds the dependency tree
entry: './src/index.js',
// 2. output property:
output: {
path: path.resolve(__dirname, 'build'), // HAS TO BE ABSOLUTE PATH. .resolve() creates a path string for any os system
// __dirname, build puts the bundle.js in a folder called 'build' in the homedir of our project
filename: 'bundle.js'
},
// 'module' property is new in webpack 2. In webpack 1 these pre-processing steps are called 'loaders', but now each 'loader' is a 'rule' in 'modules'
module: {
rules: [
// inside each rule we have an object to define the rule
{
use: 'babel-loader', // use defines which loader to use
test: /\.js/, // a regex exp that applies the loader (in this case 'babel-loader') to any file that matches the regex
}
]
}
};
module.exports = config;The .babelrc file specifies the set of rules to run when you use the babel-loader loader in webpack
{
"presets": ["babel-preset-env"]
}We can now run npm run build and get the bundle.js
// src/index.js
import sum from './sum';
const total = sum(10, 5);
console.log(total);// src/sum.js
const sum = (a, b) => a+b;
export default sum;css-loader allows us to import .css into our .js files
- the benefit of using webpack to handle css is that it allows us to load css files into our js
- Note: importing the css does not scope the css to some file. It just shows a relationship between a css file and a js one
// src/image_viewer.js
const image = document.createElement('img');
image.src = 'http://lorempixel.com/400/400';
document.body.appendChild(image);// src/index.js
import sum from './sum';
import './image_viewer.js';
// why do we just do import './image_viewer.js'? without assigning it to a variable? because we are not using it, it's just executing code
const total = sum(10, 5);
console.log(total);/* styles/ */

