The original:Webpack your bags

October 16th, 2015 by Maxime Fabre October 16th, 2017 As webpack has been updated to 3.8.1, the code package addresses of each segment have been revised.



Webpack your bags

You’ve probably heard of this cool tool called WebPack before, and if you haven’t looked at it carefully, you might be a little confused, because some people say it’s like a build tool like Gulp, and some people say it’s like a module management tool like Browserify, and if you’ve looked at it carefully, You might still be wondering what’s going on, because the official website describes Webpack as both.

To be honest, I was confused and frustrated about what webpack was at first, and I ended up shutting it down. After all, I already had a tool for building systems, and I was happy to use it. If you’ve been following javascript as closely as I have, You may have died from jumping around too much on all sorts of popular things. Fortunately, now that I have some experience, I think I can write an article to explain more clearly what WebPack is and, more importantly, what it is so good that it deserves our attention.

1. What is Webpack?

Is Webpack a build system or a module manager? Well, I’m going to get to the answer right now — the answer is both. I’m not saying it does both, but it connects them organically. Instead of building your resources and then managing your modules, WebPack treats your resources as modules themselves.

Rather, it doesn’t compile all your SCSS files, optimize all your images, import them in one place, manage all your modules, and then bring them to your page in another. Suppose you look like this:

import stylesheet from 'styles/my-styles.scss';

import logo from 'img/my-logo.svg';

import someTemplate from 'html/some-template.html';

console.log(stylesheet); // "body{font-size:12px}"console.log(logo); //"data:image/svg+xml; base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5[...] "

console.log(someTemplate) // "Hello"Copy the code

As shown above, all of your resources (whether SCSS, HTML, or other) can be treated as modules, and these modules can be imported, modified, manipulate, and packaged into your final bundle.

To do this, register loaders in your WebPack configuration file. Loaders are plug-ins that handle certain files when you encounter them. Here are some examples of loaders:

{// When you import an A.ts file, the system uses the Typescript loader to parse ittest: /\.ts/,
  loader: 'typescript'}, {// When you have images in your project, the system will compress them with the image-Webpack loader and they will be associated with data64 URLstest: /\.(png|jpg|svg)/,
  loaders: ['url'.'image-webpack'}, {// When you have SCSS files in your project, the system will parse them through the Node-sass loader and automatically prefix them with CSS (which can be directly parsed by the system)test: /\.scss/,
  loaders: ['css'.'autoprefixer'.'sass'],}Copy the code

Eventually all loaders return strings, so webpack can wrap resources as javascript modules. In this example, the SCSS file converted by Loaders looks something like this:

export default 'body{font-size:12px}';Copy the code



2. The world is so big, why do you use it?

Once you understand what WebPack is, the second question probably comes to mind: What good can it do? “Put Image and CSS in my JS? What the hell?” Well, I’ve been told for a long time to put everything in one file so we don’t waste HTTP requests.

But this leads to a major disadvantage, which is that most people now pack all their resources into a single app.js file and import that file into every page. This means that most of the time rendering each page is spent loading a lot of resources that are not used at all. If you don’t, you’ll probably have to manually import these resources to the specified page, which can lead to a tangle of dependency trees to maintain and track questions such as: Which pages need to rely on this file? Which pages will be affected by changes to A.css and B.CSS?

So both approaches are wrong, but not all of them. If we think of WebPack as an intermediary — not just a build system or a packaging tool, but an evil, intelligent packaging system that, properly configured, knows even more about your system than you do, and how best to optimize it.

3. Let’s make a small app

To make it easier for you to understand the benefits of WebPack, let’s make a small app and use WebPack to pack the resources of our app. Before doing so, I recommend using Node 4 and later and NPM 3 and later. Because good dependencies will avoid a lot of headaches when you use WebPack, if your NPM version is not new enough, you can update it by installing NPM -g.

$node --version v6.11.2 $NPM --version 5.4.2Copy the code

I also recommend that you add node_modules/. Bin to your PATH environment variable to avoid manually typing node_modules/. Bin /webpack every time, None of the following examples will show the node_modules/.bin part of the command line that I want to execute (this step is ignored when installing webpack globally). Note: Global install webpack command line: update by NPM install webpack -g.

The basic guide

To start, create a folder called Webpack-your-Bags and install WebPack in it with jquery to prove something later

$ npm init -y

$ npm install jquery --save

$ npm install webpack --save-dev

Now let’s create an app entry using the current pure ES5

Path: webpack – your – bags/SRC/index, js

var $ = require('jquery');
$('body').html('Hello');
Copy the code

Create webpack configuration file webpack.config.js, webpack.config.js is javascript, need to export an object path: webpack-your-bags/webpack.config.js

var path = require("path"); Var ROOT_PATH = path.resolve(__dirname); var ROOT_PATH = path.resolve(__dirname); Var BUILDS_PATH = path.resolve(ROOT_PATH,"builds"); // Get the full absolute path to our builds directory module.exports = {entry:'./src',
    output: {
        path: BUILDS_PATH,
        filename: 'bundle.js',}};Copy the code

Here, Entry tells WebPack which file is the entry point for your app. These are your main files and they are at the top of the dependency tree. We then tell it to build our resources into the bundle.js file in the builds directory (webpack-your-bags/builds) and I will now create the corresponding index.html path: webpack-your-bags/index.html

<! DOCTYPE html> <html> <body> <h1>My title</h1> <a>Click me</a> <script src="builds/bundle.js"></script>
</body>
</html>Copy the code

Run webpack, and if everything is fine, we’ll see a message telling us that bundle.js has been compiled correctly

$ webpack
Hash: 65d56cd1e7dddf04958b
Version: webpack 3.8.1
Time: 250ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  271 kB       0  [emitted]  [big]  main
   [0] ./src/index.js 51 bytes {0} [built]
    + 1 hidden moduleCopy the code

Here WebPack tells you that bundle.js contains our entry point (index.js) and a hidden module. This hidden module is jquery. Webpack hides third-party modules by default. We can add –display-modules

$ webpack --display-modules
Hash: 65d56cd1e7dddf04958b
Version: webpack 3.8.1
Time: 263ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  271 kB       0  [emitted]  [big]  main
   [0] ./src/index.js 51 bytes {0} [built]
   [1] ./node_modules/jquery/dist/jquery.js 268 kB {0} [built]
Copy the code

If it’s too much trouble to re-execute Webpack every time you make a change, you can also run Webpack — Watch to automatically monitor your files for changes and recompile them if they change.

Build our first loader

Remember when we talked about WebPack importing CSS and HTML and all sorts of files? When will it come in handy? Well, if you’ve made a big leap in the direction of Web components over the past few years (Angular 4, Vue, React, Polymer, X-Tag, etc.). Then you’ve probably heard the idea: Your app is much easier to maintain if it’s built with a set of reusable, self-contained UI components than if it’s a single cohesive UI — and that reusable component is a Web component (I’m keeping it simple here, so you’ll understand). Now to make the components truly self-contained, For example, consider a button. It must have some HTML in it, some JS to keep it interactive, and maybe some CSS, all of which would be perfect if they were loaded together when needed. The resource files will only be loaded when we import the Button component.

Now let’s write a button, assuming you’re already familiar with ES2015 (ES6, the new javascript standard). Since some browsers don’t support ES6 yet, we need Babel to convert ES6 to browser-supported ES5 for us. Let’s start with the loader for Babel. 1. NPM install {whatever}-loader; 2. 2. Add it to the module.loaders section of your webpack configuration file (webpack.config.js). Ok, now we need to add Babel, so install:

$ npm install babel-loader --save-dev

We also need to install Babel itself, since our example loader will not install them automatically now, so we need to install babel-core and its default ES2015 (the version of the converter that executes before the code is executed) :

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

–save-dev: packages that need to be relied upon during the development of the project, packages that need not be relied upon after the release of the project, such as our Babel. During the development process, Babel needs to convert our written ES6 code to ES5 for us. After the release, all our written ES6 code is converted to ES5, so there is no need to continue to rely on them. –save: packages that need to be relied upon after the project is released, such as jquery;

This is a JSON file that tells Babel which converter to execute on your code. Now we tell it to use es2015: path: webpack-your-bags/.babelrc

{
"presets": ["es2015"]}Copy the code

Now that Babel is configured, we can update the webpack configuration (that is, the webpack.config.js file) : What do we want? We want Babel to run on all of our.js files, but since WebPack walks through all of our dependencies, we want to avoid Babel running on third-party code like jquery, so we can filter it a little bit more, Loaders can include or exclude, be strings, regex, or callback, whichever you want to use. Since we want Babel to run only on our files, we just need to include it in our own source directory, SRC:

var path = require("path"); Var ROOT_PATH = path.resolve(__dirname); var ROOT_PATH = path.resolve(__dirname); Var BUILDS_PATH = path.resolve(ROOT_PATH,"builds"); // Get the full absolute path of our builds directory var SRC_PATH = path.resolve(ROOT_PATH,"src"); Module.exports = {entry: // get the full path to our SRC directory module.exports = {entry:'./src',
    output: {
        path: BUILDS_PATH,
        filename: 'bundle.js',
    },
    module: {
        loaders: [{
            test: /\.js/,
            loader: 'babel-loader',
            include: SRC_PATH,
        }],
    }
};
Copy the code

Now we can rewrite our index.js in ES6, and since we’ve introduced Babel, we’ll use ES6 for all the examples from here on out.

import $ from 'jquery';
$('body').html('Hello');
Copy the code

Write a widget

Now let’s write a small Button component that needs some SCSS style (SCSS is SASS 3’s new syntax, SASS is a preprocessor for CSS), an HTML template, and some JS interaction, so let’s install what we need first. We start with a very lightweight template package Mustache, and we also need to configure loaders for SCSS and HTML, because the result goes through pipes from one loader to the other, which is a little bit convoluting, how to say, like a water purifier, The SCSS we wrote is purified by the first loader into CSS, the CSS flows to the second loader through the pipe, and the CSS becomes the STYLE module that can be imported by the second loader, etc., ok, So we need a Sass-loader to “clean up” the SCSS. Once we have the CSS, there are several ways to process it. Here, we use a style-loader that takes a piece of CSS and inserts it dynamically into the page.

$ npm install mustache --save
$ npm install css-loader style-loader html-loader sass-loader node-sass --save-dev
Copy the code

To get WebPack to pipe things from one loader to another, we simply pass several loaders from right to left, using one! Or you can use an array to pass the loaders property, not the loader:

{
test: /\.js/,
loader: 'babel-loader',
include: SRC_PATH,
}, {
test: /\.scss/,
loader: 'style-loader! css-loader! sass-loader',
// Or
//loaders: ['style-loader'.'css-loader'.'sass-loader'],}, {test: /\.html/,
loader: 'html-loader',}Copy the code

Now we put the loaders are ready, let’s write a button: path: webpack – your – bags/SRC/Components/button. The SCSS

.button {background: tomato; color: white; }
Copy the code

Path: webpack – your – bags/SRC/Components/Button. The HTML

<a class="button" href="{{link}}">{{text}}</a>
Copy the code

Path: webpack – your – bags/SRC/Components/Button. Js

import $ from 'jquery';
import template from './Button.html';
import Mustache from 'mustache';
import './Button.scss';

exportdefault class Button { constructor(link) { this.link = link; } onClick(event) { event.preventDefault(); alert(this.link); } render(node) { const text = $(node).text(); // Render our $(node).html(Mustache. Render (template, {text})); // Add listener event $('.button').click(this.onClick.bind(this)); }}Copy the code

Your Button is now 100% complete. Whenever you import it, wherever you run it, it has everything you need and then renders it correctly to that place. Now we just need to render our Button via index.js to our webpage:

// import $ from 'jquery'; / / $('body').html('Hello');

import Button from './Components/Button';
const button = new Button('google.com');
button.render('a');
Copy the code

Let’s run webpack and refresh the index.html page and you should see your ugly button coming out.



Now that you’ve learned how to configure the Loader and how to define dependencies for each part of your app, this might not seem important for now as we’ll take this example a step further.

Resolution of the code

The above example is not bad because it has everything, but sometimes we don’t need our Button because some interfaces don’t have an A tag, so we don’t need to render an A tag as a Button. So we don’t even have to import the Button style, the template, Mustache and all that stuff that we’ve done up here, right? This is where we need to split the code. Code splitting is webpack’s solution to manually import buttons into the interface. That is, with WebPack you don’t have to find which pages to import and which pages not to import. The essence of code split is to define split points in your code: your code can be easily split into separate files and loaded on demand, with very simple syntax:

import $ from 'jquery'; // Ensure ([], () => {// Ensure ([], () => {// All code that needs to be imported // in a separate page'some-big-library');
  $('foo').click(() => library.doSomething());
});
Copy the code

Everything in require.ensure’s callback (that is, () => {}) is broken up into chunks of code — packages that are individually loaded via Ajax when the page needs to be loaded, which means our packages basically have these:

Bundle. Js | - jquery. Js | - index. Js / / we all files you want to import the code chunk1. Js | - some - big - libray. Js | - index - the chunk. Js / / the code in the callbackCopy the code

You don’t need to import chunk1.js, webpack loads chunk1.js only when it’s needed, which means you can split your code into chunks according to various logic. We’ll modify our index.js below to load Button only when there’s an A tag in the page:

if (document.querySelectorAll('a').length) {
    require.ensure([], () => {
        const Button = require('./Components/Button').default;
        const button = new Button('google.com');
        button.render('a');
    });
}
Copy the code

Note that when you use require, if you want to get something from the Button object export default you need to manually get it through.default because require doesn’t handle export default and exports.obj at the same time. So you have to specify which to return, but import can handle this, so it already knows (for example: Import foo from ‘bar’ to export default, import {baz} from ‘bar’ to exports.baz of ‘bar’). It’s a bit complicated here, but if you don’t understand it and want to understand it, take a closer look at ES6 and nodeJS.

Now the output of webpack should be different accordingly, let’s run webpack –display-modules –display-chunks to see which module is in which chunk

$webpack --display-modules --display-chunks Hash: c419d385603afDD301ab Version: webpack 3.8.1 Time: 1489ms Asset Size Chunks Names 0.bundle.js 307 kB 0 [emitted] [big] bundle.js 6.48 kB 1 [emitted] Main Chunk {0} 0. Bundle. Js 305 kB {1} [rendered] [1]. / SRC/Components/Button. Js 1.92 kB {0} [built] [2] ./node_modules/jquery/dist/jquery.js 268 kB {0} [built] [3] ./src/Components/Button.html 70 bytes {0} [built] [4] . / node_modules/mustache mustache. Js 19.4 kB {0} [built] [5]. / SRC/Components/Button. SCSS 1.16 kB {0} [built] [6] ./node_modules/css-loader! ./node_modules/sass-loader/lib/loader.js! The. / SRC/Components/Button. SCSS 219 bytes {0} [built] [7], / node_modules/CSS - loader/lib/CSS - base. Js 2.26 kB {0} [built] [8] /node_modules/style-loader/lib/ addstyles.js 9.41 kB {0} [built] [9]./node_modules/style-loader/lib/urls.js 3.01 kB {0} [built] chunk {1} bundle.js (main) 615 bytes [entry] [rendered] [0] ./src/index.js 615 bytes {1} [built] [0] The. / SRC/index. Js 615 bytes {1} [built] [1]. / SRC/Components/Button. Js 1.92 kB {0} [built] [2] ./node_modules/jquery/dist/jquery.js 268 kB {0} [built] [3] ./src/Components/Button.html 70 bytes {0} [built] [4] . / node_modules/mustache mustache. Js 19.4 kB {0} [built] [5]. / SRC/Components/Button. SCSS 1.16 kB {0} [built] [6] ./node_modules/css-loader! ./node_modules/sass-loader/lib/loader.js! The. / SRC/Components/Button. SCSS 219 bytes {0} [built] [7], / node_modules/CSS - loader/lib/CSS - base. Js 2.26 kB {0} [built] [8] /node_modules/style-loader/lib/ addstyles.js 9.41 kB {0} [built] [9]./node_modules/style-loader/lib/urls.js 3.01 kB {0} [built]Copy the code

You can see that our entry (bundle.js) now only has some webpack logic, the rest of the stuff (jQuery, Mustache, Button) is in 0.bundle.js, and only loads 0.bundle.js if the page has a tag on it, To let WebPack know where to find chunks when loading with Ajax, we must add a line to our configuration:

output: {
    path: BUILDS_PATH,
    filename: 'bundle.js',
    publicPath: 'builds/',},Copy the code

The output.publicPath option tells WebPack where to find the built resource relative to the current file. Now visit our page and we’ll see that everything works fine, but more importantly, we can see that, thanks to the A tag on the page, So Webpack loads exactly the code block we split out: 0.bundle.js:



If we don’t have an A tag on our page, only bundle.js will be loaded. This allows you to intelligently split up the large chunks of logic in your app so that each page only loads what it really needs. We can also name the Chunk Names of the code packages we split. Ensure can be specified by passing a third parameter to require.ensure:

require.ensure([], () => {
const Button = require('./Components/Button').default;
const button = new Button('google.com');
button.render('a');
}, 'button');
Copy the code

This makes the Chunk Names of the generated code package a button instead of a blank:

$webpack Hash: 50ed6a7993b581f0bf0a Version: webpack 3.8.1 Time: 1524 ms Asset Size Chunks Chunk Names 0. Bundle. Js 307 kB 0 [emitted] [big] button bundle. Js 6.49 kB 1 [emitted] the main [0] The. / SRC/index. Js 625 bytes {1} [built] [1]. / SRC/Components/Button. The js 1.92 kB {0} [built] [3]. / SRC/Components/Button. The HTML 70 bytes {0} [built] [5]. / SRC/Components/Button. The SCSS 1.16 kB {0} [built] [6], / node_modules/CSS - loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Button.scss 219 bytes {0} [built] + 5 hidden modulesCopy the code

Add a second component

Now have everything is very cool, but we’ll add a component to see how that: path: webpack – your – bags/SRC/Components/Header. The SCSS

.header {
  font-size: 3rem;
}
Copy the code

Path: webpack – your – bags/SRC/Components/Header. The HTML

<header class="header">{{text}}</header>
Copy the code

Path: webpack – your – bags/SRC/Components/Header. Js

import $ from 'jquery';

import Mustache from 'mustache';

import template from './Header.html';

import './Header.scss';

exportdefault class Header { render(node) { const text = $(node).text(); $(node).html( Mustache.render(template, { text }) ); }}Copy the code

We’ll render it in our index.js:

// If we have an anchor, render the Button component on it
if (document.querySelectorAll('a').length) {
    require.ensure([], () => {
        const Button = require('./Components/Button').default;
        const button = new Button('google.com');

        button.render('a');
    });
}

// If we have a title, render the Header component on it
if (document.querySelectorAll('h1').length) {
    require.ensure([], () => {
        const Header = require('./Components/Header').default;

        new Header().render('h1');
    });
}
Copy the code

Now look at the output of the webpack with –display-chunks –display-modules:

$webpack --display-modules --display-chunks Hash: 66f9e900AC553f5d66eb Version: webpack 3.8.1 Time: 1646ms Asset Size Chunks Chunk Names 0.bundle.js 306 kB 0 [emitted] [big] 1.bundle.js 307 kB 1 [emitted] [big] bundle.js 2 [6.56 kB emitted] the main chunk {0} 0. Bundle. Js 305 kB {2} [rendered] [2]. / SRC/Components/Header. Js 1.7 kB {0} [built] [3]. / node_modules/jquery/dist/jquery js 268 kB {0} {1} [built] [4], / node_modules/mustache mustache. Js 19.4 kB {0} {1} [built] [5]./node_modules/ csS-loader /lib/css-base.js 2.26 kB {0} {1} [built] [6] /node_modules/style-loader/lib/ addstyles.js 9.41 kB {0} {1} [built] [7]./node_modules/style-loader/lib/urls.js 3.01 kB {0} {1} [built] [11]. / SRC/Components/Header. The HTML bytes 62 {0} [built] [12]. / SRC/Components/Header. SCSS 1.16 kB {0} [built] [13] ./node_modules/css-loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Header.scss 199 bytes {0} [built] chunk {1} 1.bundle.js 305 kB {2} [rendered] [1] The. / SRC/Components/Button. Js 1.92 kB {1} [built] [3], / node_modules/jquery/dist/jquery js 268 kB {0} {1} [built] [4] . / node_modules/mustache mustache. Js 19.4 kB {0} {1} [built] [5], / node_modules/CSS - loader/lib/CSS - base. Js 2.26 kB {0} {1} [built] [6]./node_modules/style-loader/lib/ Addstyles.js 9.41 kB {0} {1} [built] [7] . / node_modules/style - loader/lib/urls. Js 3.01 kB {0} {1} [built] [8]. / SRC/Components/Button. The HTML bytes 70 {1} [built] [9]. / SRC/Components/Button. The SCSS 1.16 kB {1} [built] [10], / node_modules/CSS - loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Button.scss 219 bytes {1} [built] chunk {2} bundle.js (main) 601 bytes [entry] [rendered] [0] The. / SRC/index. Js 601 bytes {2} [built] [0]. / SRC/index, js 601 bytes {2} [built] [1]. / SRC/Components/Button. Js 1.92 kB {1} [built] [2] the. / SRC/Components/Header. The js 1.7 kB {0} [built] [3], / node_modules/jquery/dist/jquery js 268 kB {0} {1} [built] [4], / node_modules/mustache/mustache. Js 19.4 kB {0} {1} [built] [5], / node_modules/CSS - loader/lib/CSS - base. Js 2.26 kB {0} {1} [built] [6]./node_modules/style-loader/lib/ Addstyles.js 9.41 kB {0} {1} [built] [7] . / node_modules/style - loader/lib/urls. Js 3.01 kB {0} {1} [built] [8]. / SRC/Components/Button. The HTML bytes 70 {1} [built] [9]. / SRC/Components/Button. The SCSS 1.16 kB {1} [built] [10], / node_modules/CSS - loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Button.scss 219 bytes {1} [built] [11] ./src/Components/Header.html 62 bytes {0} [built] [12] The. / SRC/Components/Header. SCSS 1.16 kB {0} [built] [13], / node_modules/CSS - loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Header.scss 199 bytes {0} [built]Copy the code

You can see there’s a problem here: Both of our components use jQuery and Mustache, which means that the two dependencies overlap in chunks, and while WebPack does a little bit of tuning by default, it can also be used as a plugin to give WebPack more power.

Plugin is different from Loader. Instead of performing some operations on specified files, plugin processes all files and performs some more advanced operations. However, it does not have to be converted like Loader. One of the things we’re interested in at this point is CommonChunksPlugin: It analyzes your chunk’s recursive dependencies and then pulls them out and places them somewhere else, either in a completely separate file or your main file.

In our current example, we need to move the common dependencies to our entry file, and if we need jQuery and Mustache for everything, we can also move it up to the top, so let’s update our configuration file webpack.config.js:

. var webpack = require('webpack');
module.exports = {
    entry:   './src',
    output:  {
      // ...
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name:      'main'// Move dependencies to our main file children:true}),], module: {// how many times will a dependency occur before it is extracted? }};Copy the code

If we re-run webpack –display-modules –display-chunks, we can see that it is much better now because the common parts of 0.bundle.js and 1.bundle.js have been moved to bundle.js.

$webpack --display-modules --display-chunks Hash: 22fbf6bdd63c4bbbb096 Version: webpack 3.8.1 Time: 1554ms Asset Size Chunks Names 0.bundle.js 3.36 kB 0 [emitted] 1.bundle.js 3.58 kB 1 [emitted] bundle.js 310 kB 2 [emitted] [big] the main chunk {0} 0. Bundle. Js 3.12 kB {2} [rendered] [7]. / SRC/Components/Header. Js 1.7 kB {0} [built] [11] The. / SRC/Components/Header. HTML 62 bytes {0} [built] [12]. / SRC/Components/Header. SCSS 1.16 kB {0} [built] [13] ./node_modules/css-loader! ./node_modules/sass-loader/lib/loader.js! The. / SRC/Components/Header. SCSS 199 bytes {0} [built] the chunk {1} (1) bundle. Js 3.37 kB {2} [rendered] [6] The. / SRC/Components/Button. Js 1.92 kB {1} [built] [8]. / SRC/Components/Button. HTML 70 bytes {1} [built] [9] The. / SRC/Components/Button. SCSS 1.16 kB {1} [built] [10], / node_modules/CSS - loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Button.scss 219 bytes {1} [built] chunk {2} bundle.js (main) 303 kB [entry] [rendered] [0] .src /index.js 601 bytes {2} [built] [1]./node_modules/style-loader/lib/urls.js 3.01 kB {2} [built] [2] . / node_modules/jquery/dist/jquery. Js 268 kB {2} [built] [3], / node_modules/mustache mustache. Js 19.4 kB {2} [built] [4] Node_modules /css-loader/lib/css-base.js 2.26 kB {2} [built] [5]./node_modules/style-loader/lib/ addstyles.js 9.41 kB {2} [built] [0]./ SRC /index.js 601 Bytes {2} [built] [1]./node_modules/style-loader/lib/urls.js 60kb {2} [built] [2] . / node_modules/jquery/dist/jquery. Js 268 kB {2} [built] [3], / node_modules/mustache mustache. Js 19.4 kB {2} [built] [4] Node_modules /css-loader/lib/css-base.js 2.26 kB {2} [built] [5]./node_modules/style-loader/lib/ addstyles.js 9.41 kB {2} [built] [6]. / SRC/Components/Button. The js 1.92 kB {1} [built] [7]. / SRC/Components/Header. Js 1.7 kB {0} [built] [8] The. / SRC/Components/Button. HTML 70 bytes {1} [built] [9]. / SRC/Components/Button. SCSS 1.16 kB {1} [built] [10] ./node_modules/css-loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Button.scss 219 bytes {1} [built] [11] ./src/Components/Header.html 62 bytes {0} [built] [12] The. / SRC/Components/Header. SCSS 1.16 kB {0} [built] [13], / node_modules/CSS - loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Header.scss 199 bytes {0} [built]Copy the code

You can also make a common dependency load asynchronously by not providing a common chunk name or specifying async: true, and WebPack has many powerful intelligent optimizations like this. I can’t list them all, but as an exercise, let’s try to build a production version of our app.

Production and optimization

Ok, first, let’s add a few plugins to the config file, but we only want to load these plugins when NODE_ENV equals production, so let’s add some logic to the config file, because it’s only a JS file, so it’s easy:

. var webpack = require('webpack');
var production = process.env.NODE_ENV === 'production';
var plugins = [
    new webpack.optimize.CommonsChunkPlugin({
            name:      'main'// Move dependencies to our main file children:true// look for common dependencies minChunks: 2, // how many times a dependency must occur before it is extracted (i.e., how many times a dependency must be found while traversing all the chunks)}),];if(production) {plugins = plugins.concat([// production plugins are placed here]); } module.exports = { entry:'./src',
    output:  {
        path:       BUILDS_PATH,
        filename:   'bundle.js',
        publicPath: 'builds/',
    },
    plugins: plugins,
    ......
};
Copy the code

Second, Webpack has some Settings that we can also turn off in production:

.if (production) {
    plugins = plugins.concat([

        new webpack.LoaderOptionsPlugin({
            debug: true}),... ] ); } module.exports = { //debug: ! Production, // Debug mode for loaders has been completely removed in Webpack3 and above. To maintain compatibility with older loaders, Devtool: Production?false : 'eval'. }Copy the code

The first one is set to non-debug mode, which means the system won’t process much code, making it easier to debug local resources. The second one controls sourcemap generation. Webpack has several methods for rendering Sourcemap, including eval, which is the best. Production environment we don’t care much about Sourcemap, so we disable it and add our production plugins:

if(production) {plugins = plugins. Concat ([/ / the plugin will look for a similar block and file and then merge / / new webpack. Optimize the DedupePlugin (), / / webpack3 version after this has been removed / / the plugin will according to the number of modules used in your program to optimize the module / / new webpack. Optimize the OccurenceOrderPlugin (), // This plugin prevents webpack from creating blocks new webpack.optimize.m that are too small to be loaded separatelyinChunkSizePlugin({ minChunkSize: 51200, / / ~ 50 KB}), / / the plugin will minimize all eventually resources javascript code new webpack. Optimize the UglifyJsPlugin ({mangle:true,
            compress: {
                warnings: false, // prevent ugly warnings},}), // Loaders minimization mode will also be removed in Webpack3 and later // This plugin allows us to set various error variables in production, To avoid them being compiled in our last packaged code New webpack.defineplugin ({__SERVER__:! production, __DEVELOPMENT__: ! production, __DEVTOOLS__: ! production,'process.env':   {
                BABEL_ENV: JSON.stringify(process.env.NODE_ENV),
            },
        }),

    ]);
}
Copy the code

These are the ones I use most often, but WebPack provides a lot of plugins to optimize your modules and chunks, and NPM also has plugins written by others with various features.

Another aspect of resources in production is that we sometimes want to add versions to our packaged resources. Remember above we set output.filename to bundle.js. There are several variables we can use for naming, one of which is [hash], ChunkFilename represents the version of the final resource file and adds the version to chunk with output.chunkfilename. Now let’s modify our configuration file:

output: {
    path:          'builds',
    filename:      production ? '[name]-[hash].js' : 'bundle.js',
    chunkFilename: '[name]-[chunkhash].js',
    publicPath:    'builds/',},Copy the code

Since there is no good way to dynamically obtain the name of the bundle compiled by the simplified app (due to the added version number), the resources are only added to the production environment. After multiple compilations, there will be more and more packages with the version in the directory. Therefore, to save space, We also need to add a third party plugin to clean up our previous production version (path in output) every time:

$ npm install clean-webpack-plugin --save-dev

And add it to our configuration:

var webpack     = require('webpack');
var CleanPlugin = require('clean-webpack-plugin');

// ...

if(production) {plugins = plugins.concat(// Clean up the previous version/folder // compile our final resource new CleanPlugin('builds'),... }...Copy the code

OK, a few small optimizations are done, so let’s compare the results:

$webpack Hash: 8b942bcf553d3D782e6c Version: webpack 3.8.1 Time: 1542ms Asset Size Chunks 0-294790a46bbc09AFC580. js 4.34 kB 0 [emitted] 1-24fa8b23bd35ecac8a8f.js 4.57 kB 1 [emitted] bundle.js 348 kB 2 [emitted] [big] main ......Copy the code
$ NODE_ENV=production webpack
clean-webpack-plugin: D:\other\webpack-your-bags\builds has been removed.
Hash: 22674bf33e7cdefb2776
Version: webpack 3.8.1
Time: 2973ms
                       Asset    Size  Chunks             Chunk Names
main-22674bf33e7cdefb2776.js  101 kB       0  [emitted]  main
......
Copy the code

So what does Webpack do? First, because our example is very lightweight, our two asynchronous blocks are not HTTP requests, so Webpack merges them into the entry. And everything was minimized, we went from three HTTP requests totaling 356KB to one HTTP request totaling 101KB.

But webpack can erase large JS files without warning?

Yes, that’s true, but only because our application is so small. Now consider this: You have no idea what gets merged, where and when. If your split blocks suddenly have more dependencies, the split blocks will be moved to an asynchronous block instead of being merged. If the pieces are too similar to be worth loading separately, they will be merged, you just need to configure them, and WebPack will automatically optimize your application in the best way possible. You don’t have to do it manually, you don’t have to think about where or when dependencies need to be relied on, everything is fully automatic.



You may have noticed that we don’t use any Settings to minimize our HTML and CSS, because by default csS-loader and HTML-loader do this for us if the debug option is false as we mentioned earlier. This is why UglifyJsPlugin is a separate plug-in (and not a loader) : there is no JS-loader in webpack, because webpack itself is the loader for JS.

extract

Well, you may have noticed by now, since the beginning of this tutorial, that our style is already in the web page and has made a very ugly button. Now wouldn’t it be nice if we could build a final CSS file from webpack with all the styles? Of course we can, let’s do this with the help of some external plugins:

$ npm install extract-text-webpack-plugin --save-dev

Let me explain what this plugin does: This is often used with CSS, so let’s configure webpack.config.js. (Here we’ll replace module.loaders with module.rules for version change, Because in the future module.loaders will be completely replaced by module.rules) :

var webpack    = require('webpack');
var CleanPlugin = require('clean-webpack-plugin');
var ExtractPlugin = require('extract-text-webpack-plugin');
var production = process.env.NODE_ENV === 'production';

var plugins = [
    new ExtractPlugin('bundle.css'), / / < = = = where new content will be packaged webpack.optimize.Com monsChunkPlugin ({name:'main'// Move dependencies to our main file children:true// look for common dependencies minChunks: 2, // how many times a dependency must occur before it is extracted (i.e., how many times a dependency must be found while traversing all the chunks)}),]; . module.exports = { ...... plugins: plugins, module: { rules: [{test: /\.js/,
			loader: 'babel-loader',
			include: SRC_PATH,
		},{
			test: /\.html/,
			loader: 'html-loader'}, {test: /\.scss$/,
			use: ExtractPlugin.extract({
				fallback: 'style-loader',
				use: ['css-loader'.'sass-loader']
			})
		}],
        // loaders: [{
		// 	test: /\.js/,
		// 	loader: 'babel-loader',
		// 	include: SRC_PATH,
		// },{
		// 	test: /\.html/,
		// 	loader: 'html-loader', //}, // {//test: /\.scss/,
		// 	loader: 'style-loader! css-loader! sass-loader',
		// 	// Or
		// 	// loaders: ['style-loader'.'css-loader'.'sass-loader'}, //} //]}};Copy the code

Now rules’ extract method takes two arguments: the first is what happens to unextracted CSS when we are in a code block (‘style-loader’), and the second is what happens when resources are in a main file (‘ CSS-loader! Sass-loader ‘). Now if we are in a code block we can’t just magically append CSS from that block to the generated CSS before using loader, but for styles found in the main file they should be packed into a file called Builds /bundle.css. Now let’s test it out by adding a small main stylesheet to our application: webpack-your-bags/ SRC /styles.scss

body { font-family: sans-serif; Background: darken (white, 0.2); }Copy the code

Modify our index.js:

import './styles.scss'; .Copy the code

Let’s run webpack and sure enough we now have a bundle. CSS that we can now import in our HTML file:

$webpack Hash: 6890269852e3f4363514 Version: webpack 3.8.1 Time: 1607ms Asset Size Chunks 0-4a6171CA8018a700cec4.js 4.26 kB 0 [emitted] 1-ffe316264a9c9fafbb4c.js 4.58 kB 1 [emitted] bundle.js 348 kB 2 [emitted] [big] main bundle.css 70 bytes 2 [emitted] main ......Copy the code



Refreshing the page may be an error at this point because you have to merge all CSS files into one file. You can pass in the ExtractTextPlugin(‘bundle. CSS ‘, {allChunks: True}), you can also use variables in filename here, if you want a version of the style you just need to do ExtractTextPlugin(‘[name]-[hash].css’).

Load the picture

This is perfect for all of our JavaScript files right now, but one topic we haven’t talked about yet is the specific resources: how do images, fonts, and so on work and are optimized in Webpack? Let’s copy an image from the website and we’ll use it as our background because I’ve seen people put it on Geocities and it looks pretty cool:



Let’s save this image in webpack-your-bags/img/puppy.jpg and update our styles.scss accordingly:

body{
	font-family:sans-serif;
	background:url('.. /img/puppy.jpg') no-repeat 0 0;
	background-size:50%;
}
Copy the code

If you do, Webpack will justifiably tell you “what PNG is it doing?” because we didn’t load it. There are two native loaders that we can use to handle specific resources: file-loader and url-loader: the first will only return the URL of a resource with no special changes, giving you a version file generated in the process (which is the default behavior); The second inlines the resource to a data:image/ JPEG; Address of Base64.

In fact, the two plugins are not antithetical: for example, if your background is a 2MB image, don’t inline it and load it separately. On the other hand, if it’s a small 2KB icon, it’s best to inline it to save HTTP requests. So we set up two:

$ npm install url-loader file-loader --save-dev

{
    test: /\.(png|gif|jpe? g|svg)$/i, loader:'url-loader? limit=10000',},Copy the code

In this case, we pass a limit query parameter to urL-loader telling it to inline if the asset is larger than 10KB or less, otherwise, we fall back to file-loader and reference it. This syntax is called a query string, and you use it to configure the loader, or you can configure the loader from an object:

{
    test: /\.(png|gif|jpe? g|svg)$/i, loader:'url-loader',
    query: {
      limit: 10000}}Copy the code

Well, let’s look at the results:

$webpack Hash: e672f7becf7b049e759d Version: webpack 3.8.1 Time: 1653ms Asset Size Chunks 0-4a6171CA8018a700cec4.js 4.26 kB 0 [emitted] 1-ffe316264a9c9fafbb4c.js 4.58 kB 1 Even though it is emitted by the world at least once a year, it still remains an essential dilemma. [emitted] bundle.js 348 kB 2 [emitted] [big] main bundle. CSS 2.88 kB 2 [emitted] mainCopy the code

As we can see: none of the JPG is packed, because our puppy image (2KB) is smaller than the configured size (10KB), so it is inline. This means that if we visit our website, we can bask in the glory of our canine overlords without loading images.



This is very powerful, because it means that WebPack can now intelligently optimize specific resources based on the ratio of size to HTTP requests. With a good loader, you can even go one step further and link everything together. The most common one is image-loader, which compresses all images before packaging them. It even has one? The bypassOnDebug query parameter lets you do this only at production time. There are many such plug-ins, and I encourage you to look at them at the end of this article.

We’re going to bring it to life now

What we’ve done above is just to show the power of WebPack, but now let’s focus more on native code debugging. One of the biggest bugs you probably notice often when it comes to build tools is real-time loading: LiveReload, BrowserSync, don’t have to wait too long. But when we change something we want the whole page to refresh automatically, so that it does what we want it to do in one step is called module replacement or hot reload. The idea is that since WebPack knows the location of every module in the tree we depend on, a small change can be easily patched up with a new file. More specifically, the changes you make appear on the page in real time but you don’t notice that the page has been refreshed.

In order to use HMR (hot module loading), we need a server that can serve our hot resources. There is a built-in dev-server in WebPack that we can take advantage of, so let’s install it:

$ npm install webpack-dev-server --save-dev

Now to run the server we developed, it doesn’t get much easier than this, just run the following command:

$ webpack-dev-server

Now, please let us access to the web server at http://localhost:8080/webpack-dev-server/. You’ll see your usual page, but now let’s modify a SASS file like magic:



Now, whenever we run Webpack-dev-server it is already in HMR mode. Please note that we use webpack-dev-server here to service our hot resources, but you can also use several other options like Express Server. Webpack provides a middleware, and you can use this middleware to move the plug and play HMR to another server.

Clean or dead

If you’ve been following this tutorial, you may have noticed something odd: Why are loaders nested in module.rules(module.loaders) but plug-ins not? Of course this is because there are other things you can throw into the Module! Webpack doesn’t just have loaders, it also has pre-loaders, post-loaders: our main loaders before or after code execution. Let’s take an example: I’m sure the code I wrote this article is horrible, so we apply ESLint to our code before we modify it:

$ npm install eslint eslint-loader babel-eslint --save-dev

Now let’s create a simple.eslintrc file that I know will fail later: path: webpack-your-bags/.eslintrc

{
    "parser": "babel-eslint"."rules": {
        "quotes": 2}}Copy the code

Now to add our preloader, we just use the same syntax as before, but in module.preLoaders (removed after WebPack2), Rules enfore () :

module:  {
        // preLoaders: [{
        //     test: /\.js/,
        //     loader: 'eslint-loader',
        // }],
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            enforce: "pre",
            loader: "eslint-loader"}}],Copy the code

Now, if we run Webpack, sure enough it fails:

$webpack Hash: 891f6c68504d0f787AFA Version: webpack 3.8.1 Time: 2335ms Asset Size Chunks 0-4a6171CA8018a700cec4.js 4.26 kB 0 [emitted] 1-ffe316264a9c9fafbb4c.js 4.58 kB 1 Emitted emitted by the world at least once a year [emitted by the world at least once a year] [emitted by the world at least once a year] [emitted by the world at least once a year] [emitted by the world at least once a year] The error [built] [1] [1]. / SRC/styles. The SCSS 41 bytes {2} [built] [7]. / SRC/Components/Button. Js 1.92 kB {1} [built] [1 The error] [8]. / SRC/Components/Header. The js 1.7 kB {0} [built] error [1] [9]. / SRC/Components/Button. The HTML 70 bytes {1} [built] [10]. / SRC/Components/Button. The SCSS 1.16 kB {1} [built] [11], / node_modules/CSS - loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Button.scss 219 bytes {1} [built] [12] ./src/Components/Header.html 62 bytes {0} [built] [13] The. / SRC/Components/Header. SCSS 1.16 kB {0} [built] [14], / node_modules/CSS - loader! ./node_modules/sass-loader/lib/loader.js! ./src/Components/Header.scss 199 bytes {0} [built] [15] ./node_modules/css-loader! ./node_modules/sass-loader/lib/loader.js! ./ SRC /styles.scss 297 bytes [built] [16]./img/puppy.jpg 2.8 kB [built] + 5 Hidden Modules ERRORin ./src/Components/Button.js

D:\other\webpack-your-bags\src\Components\Button.js
   1:15  error  Strings must use doublequote  quotes
   2:22  error  Strings must use doublequote  quotes
   3:22  error  Strings must use doublequote  quotes
   4:8   error  Strings must use doublequote  quotes
  25:11  error  Strings must use doublequote  quotes
......
Copy the code

Error: Strings must use doublequote quotes If not, comment out the esLint content and proceed with the following. Let’s run another example of a pre-loader: for each component we import a template with the same name and style sheet. Let’s use a preloader to automatically load any file with the same name as a module:

$ npm install baggage-loader --save-dev

{
	test: /\.js/,
	enforce: "pre",
    loader: 'baggage-loader? [file].html=template&[file].scss'
}
Copy the code

This tells WebPack that if you encounter an HTML file with the same name, import it as template, along with any SASS file with the same name. Now we can change our component from this:

from

import $ from 'jquery';
import template from './Button.html';
import Mustache from 'mustache';
import './Button.scss';
Copy the code

To:

import $ from 'jquery';
import Mustache from 'mustache';
Copy the code

As you can see with such a powerful pre-loader, so is the post-loader. Take a look at the list of available loaders at the end of this article and you are sure to find many use cases in it. Take a look at the list of available loaders at the end of this article and you’ll definitely find more cases.

additional

For webpack-dev-server we can not only start our web server by running the command line webpack-dev-server directly, but also configure the shortcut command line in package.json file scripts, such as:

"scripts": {
  "start:dev": "webpack-dev-server"
}
Copy the code

We can then start our Web server directly by running the command line NPM run start:dev (equivalent to webpack-dev-server).

Would you like to know more?

Currently, our application is fairly small so we still don’t see the benefits of WebPack, but as the application gets bigger, WebPack will become useful and you’ll get more insight into what our actual dependency tree is. We might get it right or wrong, when our application gets stuck, and so on. Now inside the application, WebPack knows everything, but you have to have the courtesy to let it show what it knows. You can run the following command by generating a configuration file:

webpack --profile --json > stats.json

The first flag (–profile) tells WebPack to generate a configuration file, the second flag (–json) produces its JSON, and the last flag (> stats.json) we let it all go to a JSON file. There are many websites that analyze these profiles, but Webpack provides an official explanation of this information. So go to Webpack to analyze and import your JSON file. Now go to the Modules TAB and you should see a visual representation of your dependency tree:



The redder a dot is, the more it affects your final bag. In our case, this point is Jquery, because it is the most important of all our modules. Look at all the tabs, look around, you’re not going to learn much in our little application, but this tool is really important because it gives you insight into your dependency tree and your ultimate resources. Now, as I said, there are other services that can provide insight into your configuration files. Another one I like is Webpack Visualizer: It uses a pie chart to show what space your application’s dependencies are taking up, and of course it can also represent our case:



That’s all we have

As far as I know, Webpack has completely replaced Grunt and gulp: I used them mostly but now I use Webpack, and for the rest I just use NPM scripts. For each example, we used to have a common task of converting our API documents to HTML via Aglio, which is easy to do, like this we modify our package.json file:

{
  "scripts": {
    "build": "webpack"."build:api": "aglio -i docs/api/index.apib -o docs/api/index.html"}}Copy the code

But if you have more complex tasks with packaging and resources in your gulp, Webpack will work better in other system builds. Look at this example of how to integrate Webpack into gulp:

var gulp = require('gulp');
var gutil = require('gutil');
var webpack = require('webpack');
var config = require('./webpack.config');

gulp.task('default'.function(callback) {
  webpack(config, function(error, stats) {
    if (error) throw new gutil.PluginError('webpack', error);
    gutil.log('[webpack]', stats.toString());

    callback();
  });
});
Copy the code

And because webPack also has node apis, it’s easy to use in other build systems, and in any case, you’ll find its ideas everywhere.

Anyway, I think this is a good enough aerial view of the Webpack to do for you. You might think we’ve covered a lot in this article, but we think it’s just scratching the surface: multiple entry points, prefetching, background changes, etc. Webpack is an impressive and powerful tool, certainly because it’s more opaque than traditional build tools, and I wouldn’t deny that. However, once you know how to tame it, it will make a sweet sound into your ears and you will savor it forever. I’ve used it on several projects, it provides optimization and automation, and I can’t imagine how many times I have to beat my head back and forth to know when and where the required resources are.

resources

  • Webpack document
  • Loader list
  • The plugin list
  • The source of this article
  • Webpack configuration

note

The code package of each section has been uploaded to GitHub.

Github.com/hrh94/Webpa…