A team of my own is currently writing an interview atlas, which will be open sourced in mid-July. The content is very rich, the first version will open source front-end knowledge and programmers necessary knowledge, the latter will gradually write back-end knowledge. Because the project involves too much content (it has been written for one and a half months) and needs to be translated into English, it takes a long time. Interested students can Follow my Github to get the fastest update message.

This article uses Webpack version 3.6.0 and is divided into two parts. The first step is to simply use WebPack, and the second part is to configure WebPack through a real project, without using any CLI, step by step until the production code is packaged. This is the warehouse corresponding to this project, and each section basically corresponds to a COMMIT.

This is the outline of the article, if you find it interesting you can read on

What exactly is Webpack

Since the advent of modularity, it has been possible to separate chunks of code into individual modules, but this has created a problem. Each JS file needs to be fetched from the server, which causes slow loading. The main purpose of Webpack is to solve this problem by packing all the small files into one or more large files. The pictures on the website illustrate this very well, but Webpack is also a tool that allows you to use a variety of new front-end technologies.

Simple to use

The installation

Enter them in sequence on the command line

mkdir  webpack-demo
cd webpack-demo
// Create package.json, which will ask some questions, just press Enter to skip it
npm init 
// This installation method is recommended, of course, you can also install the global environment
// This installation puts WebPack into the devDependencies dependency
npm install --save-dev webpack
Copy the code

Then create the file as shown below

Write the code in the following file

// sum.js
// This modular notation is unique to the Node environment and is not supported by browsers natively
module.exports = function(a, b) {
    return a + b
}
// index.js
var sum = require('./sum')
console.log(sum(1.2))
Copy the code

      
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
    <script src="./build/bundle.js"></script>
</body>
</html>
Copy the code

Now let’s start configuring the simplest webpack, first creating the webpack.config.js file and then writing the following code

// A built-in library
const path = require('path')
module.exports = {
    entry:  './app/index.js'.// Import file
    output: {
      path: path.resolve(__dirname, 'build'), // Must use absolute address, output folder
      filename: "bundle.js" // Output the file name after packaging}}Copy the code

Now we are ready to use Webpack, typing on the command line

node_modules/.bin/webpack
Copy the code

You should be able to see something like that

Webpack must be doing something. Let’s take a look at the bundle.js file.

After simplifying the code, the idea is this

var array = [(function () {
        var sum = array[1]
        console.log(sum(1.2))}), (function (a,b) {
        return a + b
    })
]
array[0] ()/ / - > 3
Copy the code

Because the module.export browser is not supported, Webpack changes the code to something the browser can recognize. Now open the index.html file in your browser and you should see the correct log as well.

We installed webpack in the folder before, it is too tedious to input node_modules/.bin/webpack each time, you can modify it in package.json as follows

"scripts": {
    "start": "webpack"
  },
Copy the code

Then run NPM run start again and you will see the same effect as before. So much for simple use, let’s explore more of WebPack’s features.

Loader

Loader is a powerful feature of Webpack, which allows you to use many new technologies.

Babel

Babel lets you write code in ES2015/16/17 without worrying about browser problems. Babel can convert code for you. Start by installing the necessary Babel libraries

npm i --save-dev babel-loader babel-core babel-preset-env
Copy the code

Let’s start with the three libraries we installed

  • Babel-loader is used to let Webpack know how to run Babel
  • Babel-core is kind of a compiler, it’s a library that knows how to parse code, right
  • The library babel-preset-env can be used to transform the code according to the environment

Next change the code in webpack-config.js

module.exports = {
/ /...
    module: {
        rules: [{// Babel is used for js files
                test: /\.js$/.// Which loader to use
                use: 'babel-loader'.// Paths are not included
                exclude: /node_modules/}}}]Copy the code

There are many ways to configure Babel, and the.babelrc file management is recommended here.

/ /.. babelrc
{
    "presets": ["babel-preset-env"]}Copy the code

Now change the JS code to ES6

// sum.js
export default (a, b) => {
    return a + b
}
// index.js
import sum from './sum'
console.log(sum(1.2))
Copy the code

Execute NPM run start and look at the bundle.js code. You can see that the code has been converted and will print 3 as usual.

Of course, Babel is much more than that, so if you’re interested, go to the official website to explore for yourself.

Processing images

In this section we will use url-loader and file-loader, both of which can handle images as well as other functions for those interested in learning on their own.

To install the library

npm i --save-dev url-loader file-loader
Copy the code

Create an images folder, place two images, and create a JS file to process images under the APP folder. The current folder structure is shown in the figure below

// addImage.js
let smallImg = document.createElement('img')
// require to come in
smallImg.src = require('.. /images/small.jpeg')
document.body.appendChild(smallImg)

let bigImg = document.createElement('img')
bigImg.src = require('.. /images/big.jpeg')
document.body.appendChild(bigImg)
Copy the code

Next modify the webpack.config.js code

module.exports = {
// ...
    module: {
        rules: [
            // ...
            {
            // The image format is regular
                test: /\.(png|jpe? g|gif|svg)(\? . *)? $/.use: [{loader: 'url-loader'.// Configure the url-loader option
                    options: {
                    // Limit the image size to 10000B, less than the limit will be converted to Base64 format
                      limit: 10000.// Out of limit, create file format
                    // build/images/[image name].[hash].
                      name: 'images/[name].[hash].[ext]'}}]}}Copy the code

Run NPM run start and the package succeeds as shown in the following figure

You can see that large images are extracted separately and small images are packed into bundle.js.

When I open the HTML file in the browser, I find that the small image is actually displayed, but I don’t see the large image. When I open the developer toolbar, I find that there is a problem with the image path of our large image, so we need to modify the webpack.config.js code again.

module.exports = {
    entry:  './app/index.js'.// Import file
    output: {
      path: path.resolve(__dirname, 'build'), // Must use absolute address, output folder
      filename: "bundle.js".// Output the file name after packaging
      publicPath: 'build/' // Know how to find resources
    }
    // ...
  }
Copy the code

Finally, run NPM run start, compile successfully, refresh the page again, you can find that the large image is displayed correctly this time. The next section will show you how to handle CSS files.

Working with CSS files

Add the styles folder, add the addimage. CSS file, and add the code to that file

img {
    border: 5px black solid;
}
.test {border: 5pxblack solid; }Copy the code

In this section we start with the CSS-loader and style-loader libraries. The former allows CSS files to support IMPOST and parse CSS files, while the latter can insert parsed CSS into HTML as tags, so the latter relies on the former.

npm i --save-dev css-loader style-loader
Copy the code

First modify the addimage. js file

import '.. /styles/addImage.css'

let smallImg = document.createElement('img')
smallImg.src = require('.. /images/small.jpeg')
document.body.appendChild(smallImg)

// let bigImg = document.createElement('img')
// bigImg.src = require('.. /images/big.jpeg')
// document.body.appendChild(bigImg)
Copy the code

Then modify the webpack.config.js code

module.exports = {
// ...
    module: {
      rules: [{test: /\.css$/.use: ['style-loader',
                {
                    loader: 'css-loader'.options: {
                        modules: true}}]},]}}Copy the code

Run NPM run start and refresh the page to see that the image is properly framed. Now let’s look at the HTML file structure

As you can see from the image above, the code we wrote in addimage. CSS was added to the style tag, and since we turned on the CSS modularization option,.test was converted to a unique hash, which solved the CSS variable name duplication problem.

However, there are drawbacks to incorporating CSS code into JS files. Large amounts of CSS code can cause large size of JS files, and DOM manipulation can cause performance problems. So next we’ll use the extract-text-webpack-plugin to package the CSS file as a separate file

Install NPM I –save-dev extract-text-webpack-plugin first

Then modify the webpack.config.js code

const ExtractTextPlugin = require("extract-text-webpack-plugin")

module.exports = {
/ /...
    module: {
      rules: [{test: /\.css$/.// Write the same as before
          loader: ExtractTextPlugin.extract({
          // This must be done, otherwise an error will be reported
                fallback: 'style-loader'.use: [{
                    loader: 'css-loader'.options: { 
                        modules: true}}]})]}]},// List of plug-ins
    plugins: [
    // The path to the output file
      new ExtractTextPlugin("css/[name].[hash].css")]}Copy the code

Run NPM run start to see that the CSS file is packaged separately

But when we refresh the page at this time, we will find that the border of the image disappears. That is because our HTML file does not reference the new CSS file, so we need to introduce the new file manually. In the following chapter, we will automatically introduce the new file through plug-in.

Next, we will continue our Webpack study with a project. Before that, we will clone the project. The original address of the project is here, because the version of Webpack used is too low, and there are some problems with the libraries I rely on, so I copied the project and modified the version numbers of several libraries.

Please follow the following code in sequence

git clone https://github.com/KieSun/webpack-demo.git
cd// Switch to the 0.1 TAB and create a new branch git checkout -b demo 0.1cdProject NPM I // Check whether the branch is demo and proceed to the next step if there is no problemCopy the code

How do I use WebPack in my project

The project is already configured with a very simple Babel and Webpack, just run NPM Run build

At this point, you’ll find that the bundle.js size is unacceptable, so the main purpose of the next chapter is to split a single file into multiple files and optimize the project.

The separation of code

Let’s consider the caching mechanism first. Dependence on code libraries will seldom go to take the initiative to upgrade version, but our own code every time the change, so we can consider to will rely on libraries apart from your own code, so that users can avoid as far as possible when it is applied in the next use repeated download without code changes, so will depend on the extracted code, We need to change some codes of entrance and exit.

Json < span style = "box-sizing: border-box
const VENOR = ["faker"."lodash"."react"."react-dom"."react-input-range"."react-redux"."redux"."redux-form"."redux-thunk"
]

module.exports = {
// We used the single file entry before
// Entry also supports multiple file entries. Now we have two entries
// One is our own code, and one is the code that depends on the library
  entry: {
  // Bundle and vendor are arbitrarily named and mapped to [name]
    bundle: './src/index.js'.vendor: VENOR
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js'
  },
  // ...
 }
Copy the code

Now let’s build and see if there are any surprises

It’s a real surprise. Why doesn’t the bundle size change at all? This is because the bundle also introduces code that depends on the library. The previous steps did not extract the code introduced in the bundle. Now let’s learn how to extract the common code.

Extract common code

In this section we use the CommonsChunkPlugin that comes with WebPack.

module.exports = {
/ /...
  output: {
    path: path.join(__dirname, 'dist'),
    // Since we want the cache to work, we should change the file name every time we change the code
    // [chunkhash] will automatically change the hash based on whether the file has changed
    filename: '[name].[chunkhash].js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
    // Vendor means the same as before
    // The manifest file is a separate extraction of the things that change with each packaging, so that the code that does not change does not need to be repackaged, which can speed up the packaging
      names: ['vendor'.'manifest'].// Use with the manifest file
      minChunks: Infinity}})];Copy the code

When we rebuild, we see that the bundle has been significantly reduced in size

But when we used hashing to ensure caching and found that each build generated a different file, we introduced another plug-in to help us remove unwanted files.

npm install --save-dev clean-webpack-plugin
Copy the code

Then modify the configuration file

module.exports = {
/ /...
  plugins: [
  // Delete only the bundle and manifest files in the dist folder
    new CleanWebpackPlugin(['dist/bundle.*.js'.'dist/manifest.*.js'] and {/ / print the log
      verbose: true.// Delete files
      dry: false})]};Copy the code

The above files will be deleted during build.

Since we are now packing the file into three JS files, and there may be more in the future, we need to manually add tags to the HTML every time we add a JS file. Now we can do this automatically through a plug-in.

npm install html-webpack-plugin --save-dev
Copy the code

Then modify the configuration file

module.exports = {
/ /...
  plugins: [
  // We use the previous HTML file as the template
  // Note that in the previous HTML file, please be sure to delete the JS file introduced earlier
    new HtmlWebpackPlugin({
      template: 'index.html'}})];Copy the code

When you perform the build operation, you will find that the HTML file has been generated and the JS file has been automatically imported

Load the code on demand

In this section we will learn how to load the code on demand. In the previous vendor entry I found that I forgot to add the Router library. You can add the router library and rebuild it, and you will find that the bundle is less than 300KB.

Now our bundle contains all of our own code. However, when the user visits our home page, we do not need to let the user load the code other than the home page at all. This optimization can be accomplished through asynchronous loading of the route.

Now modify SRC /router.js

Note that in the latest version of V4 routing, the on-demand loading method has been changed. If V4 is installed, you can go to the official website to learn
import React from 'react';
import { Router, Route, IndexRoute, hashHistory } from 'react-router';

import Home from './components/Home';
import ArtistMain from './components/artists/ArtistMain';

const rootRoute = {
  component: Home,
  path: '/'.indexRoute: { component: ArtistMain },
  childRoutes: [{path: 'artists/new',
      getComponent(location, cb) {
        System.import('./components/artists/ArtistCreate')
          .then(module= > cb(null.module.default))
      }
    },
    {
      path: 'artists/:id/edit',
      getComponent(location, cb) {
        System.import('./components/artists/ArtistEdit')
          .then(module= > cb(null.module.default))
      }
    },
    {
      path: 'artists/:id',
      getComponent(location, cb) {
        System.import('./components/artists/ArtistDetail')
          .then(module= > cb(null.module.default))
      }
    }
  ]
}

const Routes = (a)= > {
  return (
    <Router history={hashHistory} routes={rootRoute} />
  );
};

export default Routes;
Copy the code

Then run the build command, and you can see that our bundle has been slimmed down and added several new files

Open the HTML file in the browser, and when clicking route jump, you can see that a JS file is loaded in the Network column of developer tools.

Home page

Click Random Artist in the upper right corner

Automatically refresh

This section explains how to use the auto-refresh feature, which requires a cumbersome wait while you build each update.

First install the plug-in

npm i --save-dev webpack-dev-server
Copy the code

Then modify the packet.json file

"scripts": {
    "build": "webpack"."dev": "webpack-dev-server --open"
  },
Copy the code

Now directly executing NPM run dev shows that the browser automatically opens an empty page and has new output on the command line

After the compilation is complete, modify the JS or CSS file and find that WebPack automatically completes the compilation for us and only updates the code that needs to be updated

However, every time the page is refreshed, it is not friendly to debug, and module hot replacement is needed. However, because React is used in the project, and Vue and other frameworks have their own hot-loader, I will skip it here. If you are interested, you can learn it for yourself.

Generate production environment code

Now we can combine what we’ve learned with some new plug-ins to build production code.

npm i --save-dev url-loader optimize-css-assets-webpack-plugin file-loader extract-text-webpack-plugin
Copy the code

Modify the WebPack configuration

var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

const VENOR = ["faker"."lodash"."react"."react-dom"."react-input-range"."react-redux"."redux"."redux-form"."redux-thunk"."react-router"
]

module.exports = {
  entry: {
    bundle: './src/index.js'.vendor: VENOR
  },
  // If you want to modify the webpack-dev-server configuration, modify it in this object
  devServer: {
    port: 8081
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [{
        test: /\.js$/.use: 'babel-loader'
      },
      {
        test: /\.(png|jpe? g|gif|svg)(\? . *)? $/.use: [{
            loader: 'url-loader'.options: {
                limit: 10000.name: 'images/[name].[hash:7].[ext]'}}}, {test: /\.css$/.loader: ExtractTextPlugin.extract({
            fallback: 'style-loader'.use: [{
            // You can also use postcss to handle the CSS code first
                loader: 'css-loader'}]})},]},plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor'.'manifest'].minChunks: Infinity
    }),
    new CleanWebpackPlugin(['dist/*.js'] and {verbose: true.dry: false
    }),
    new HtmlWebpackPlugin({
      template: 'index.html'
    }),
    // Generate global variables
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify("process.env.NODE_ENV")}),// Separate the CSS code
    new ExtractTextPlugin("css/[name].[contenthash].css"),
    // Compress extracted CSS, and solve the problem of JS repetition separated by ExtractTextPlugin
    new OptimizeCSSPlugin({
      cssProcessorOptions: {
        safe: true}}),// Compress the JS code
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false}}})];Copy the code

Modify packet.json file

"scripts": {
    "build": "NODE_ENV=production webpack -p"."dev": "webpack-dev-server --open"
  }
Copy the code

Run NPM run build

You can see that after all these steps we have reduced the bundle to just 27.1KB. Common libraries such as Vendor can be linked in using CDN.

supplement

There are some useful little points about webPack configuration that were not mentioned above, but are covered here.

module.exports = {
  resolve: {
  // The extension of the file is not required for each file
    extensions: ['.js'.'.css'.'.json'].// Path alias, for example CSS can be used to point to the static/ CSS path
    alias: {
      The '@': resolve('src'),
      'css': resolve('static/css')}},// Generate a source-map for the break point, where there are several options
  devtool: '#cheap-module-eval-source-map',}Copy the code

Afterword.

If you’ve followed the step-by-step instructions in this article, you should be able to understand most of the WebPack configuration and know how to configure it yourself. Thank you for looking at this. This is the repository for this project. Each section basically has a commit.

The article is long and it is hard to avoid mistakes. If you find any problems or I don’t understand any expressions, you can leave a message to me.