Giới thiệu về Webpack

Webpack là một công cụ đã được chú ý rất nhiều trong vài năm gần đây và hiện nay nó được sử dụng trong hầu hết các dự án. Tìm hiểu về nó.

Webpack là gì?

Webpack là một công cụ cho phép bạn biên dịch các mô-đun JavaScript, còn được gọi làgói mô-đun.

Với một số lượng lớn tệp, nó tạo ra một tệp (hoặc một vài tệp) chạy ứng dụng của bạn.

Nó có thể thực hiện nhiều hoạt động:

  • giúp bạn nhóm các tài nguyên của mình.
  • theo dõi các thay đổi và chạy lại các tác vụ.
  • có thể chạy chuyển đổi Babel sang ES5, cho phép bạn sử dụng phiên bản mới nhấtJavaScriptcác tính năng mà không cần lo lắng về sự hỗ trợ của trình duyệt.
  • có thể chuyển đổi CoffeeScript sang JavaScript
  • có thể chuyển đổi hình ảnh nội tuyến sang URI dữ liệu.
  • cho phép bạn sử dụng request () cho các tệp CSS.
  • có thể chạy một máy chủ web phát triển.
  • có thể xử lý thay thế mô-đun nóng.
  • có thể chia các tệp đầu ra thành nhiều tệp, để tránh có một tệp js lớn để tải trong lần truy cập trang đầu tiên.
  • Có thể biểu diễncây rung rinh.

Webpack không bị giới hạn trong việc sử dụng trên giao diện người dùng, nó cũng hữu ích trong quá trình phát triển Node.js phụ trợ.

Tiền thân của webpack và các công cụ vẫn được sử dụng rộng rãi, bao gồm:

  • Tiếng càu nhàu
  • Bông cải xanh
  • Nuốt chửng

Có rất nhiều điểm tương đồng về những gì mà chúng và Webpack có thể làm, nhưng sự khác biệt chính là chúng được gọi làngười chạy nhiệm vụ, trong khi webpack được sinh ra như một trình gói mô-đun.

Đó là một công cụ tập trung hơn: bạn chỉ định một điểm vào cho ứng dụng của mình (thậm chí nó có thể là một tệp HTML với các thẻ script) và webpack phân tích các tệp và gói tất cả những gì bạn cần để chạy ứng dụng trong một tệp đầu ra JavaScript duy nhất (hoặc hơn thế nữa nếu bạn sử dụng tách mã).

Cài đặt webpack

Webpack có thể được cài đặt toàn cầu hoặc cục bộ cho mỗi dự án.

Cài đặt toàn cầu

Đây là cách cài đặt nó trên toàn cầu vớiSợi:

yarn global add webpack webpack-cli

with npm:

npm i -g webpack webpack-cli

once this is done, you should be able to run

webpack-cli

Running webpack-cli

Local install

Webpack can be installed locally as well. It’s the recommended setup, because webpack can be updated per-project, and you have less resistance to using the latest features just for a small project rather than updating all the projects you have that use webpack.

With Yarn:

yarn add webpack webpack-cli -D

with npm:

npm i webpack webpack-cli --save-dev

Once this is done, add this to your package.json file:

{
  //...
  "scripts": {
    "build": "webpack"
  }
}

once this is done, you can run webpack by typing

yarn build

in the project root.

Webpack configuration

By default, webpack (starting from version 4) does not require any config if you respect these conventions:

  • the entry point of your app is ./src/index.js
  • the output is put in ./dist/main.js.
  • Webpack works in production mode

You can customize every little bit of webpack of course, when you need. The webpack configuration is stored in the webpack.config.js file, in the project root folder.

The entry point

By default the entry point is ./src/index.js This simple example uses the ./index.js file as a starting point:

module.exports = {
  /*...*/
  entry: './index.js'
  /*...*/
}

The output

By default the output is generated in ./dist/main.js. This example puts the output bundle into app.js:

module.exports = {
  /*...*/
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.js'
  }
  /*...*/
}

Loaders

Using webpack allows you to use import or require statements in your JavaScript code to not just include other JavaScript, but any kind of file, for example CSS.

Webpack aims to handle all our dependencies, not just JavaScript, and loaders are one way to do that.

For example, in your code you can use:

import 'style.css'

by using this loader configuration:

module.exports = {
  /*...*/
  module: {
    rules: [
      { test: /\.css$/, use: 'css-loader' },
    ]
  }
  /*...*/
}

The regular expression targets any CSS file.

A loader can have options:

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

You can require multiple loaders for each rule:

module.exports = {
  /*...*/
  module: {
    rules: [
      {
        test: /\.css$/,
        use:
          [
            'style-loader',
            'css-loader',
          ]
      }
    ]
  }
  /*...*/
}

In this example, css-loader interprets the import 'style.css' directive in the CSS. style-loader is then responsible for injecting that CSS in the DOM, using a <style> tag.

The order matters, and it’s reversed (the last is executed first).

What kind of loaders are there? Many! You can find the full list here.

A commonly used loader is Babel, which is used to transpile modern JavaScript to ES5 code:

module.exports = {
  /*...*/
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
  /*...*/
}

This example makes Babel preprocess all our React/JSX files:

module.exports = {
  /*...*/
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  },
  resolve: {
    extensions: [
      '.js',
      '.jsx'
    ]
  }
  /*...*/
}

See the babel-loader options here.

Plugins

Plugins are like loaders, but on steroids. They can do things that loaders can’t do, and they are the main building block of webpack.

Take this example:

module.exports = {
  /*...*/
  plugins: [
    new HTMLWebpackPlugin()
  ]
  /*...*/
}

The HTMLWebpackPlugin plugin has the job of automatically creating an HTML file, adding the output JS bundle path, so the JavaScript is ready to be served.

There are lots of plugins available.

One useful plugin, CleanWebpackPlugin, can be used to clear the dist/ folder before creating any output, so you don’t leave files around when you change the name of the output file:

module.exports = {
  /*...*/
  plugins: [
    new CleanWebpackPlugin(['dist']),
  ]
  /*...*/
}

The webpack mode

This mode (introduced in webpack 4) sets the environment on which webpack works. It can be set to development or production (defaults to production, so you only set it when moving to development)

module.exports = {
  entry: './index.js',
  mode: 'development',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.js'
  }
}

Development mode:

  • builds very fast
  • is less optimized than production
  • does not remove comments
  • provides more detailed error messages and suggestions
  • provides a better debugging experience

Production mode is slower to build, since it needs to generate a more optimized bundle. The resulting JavaScript file is smaller in size, as it removes many things that are not needed in production.

I made a sample app that just prints a console.log statement.

Here’s the production bundle:

Production bundle

Here’s the development bundle:

Development bundle

Running webpack

Webpack can be run from the command line manually if installed globally, but generally you write a script inside the package.json file, which is then run using npm or yarn.

For example this package.json scripts definition we used before:

"scripts": {
  "build": "webpack"
}

allows us to run webpack by running

npm run build

or

yarn run build

or

yarn build

Watching changes

Webpack can automatically rebuild the bundle when a change in your app happens, and keep listening for the next change.

Just add this script:

"scripts": {
  "watch": "webpack --watch"
}

and run

npm run watch

or

yarn run watch

or

yarn watch

One nice feature of the watch mode is that the bundle is only changed if the build has no errors. If there are errors, watch will keep listening for changes, and try to rebuild the bundle, but the current, working bundle is not affected by those problematic builds.

Handling images

Webpack allows us to use images in a very convenient way, using the file-loader loader.

This simple configuration:

module.exports = {
  /*...*/
  module: {
    rules: [
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
          'file-loader'
        ]
      }
    ]
  }
  /*...*/
}

Allows you to import images in your JavaScript:

import Icon from './icon.png'

const img = new Image() img.src = Icon element.appendChild(img)

(img is an HTMLImageElement. Check the Image docs)

file-loader can handle other asset types as well, like fonts, CSV files, xml, and more.

Another nice tool to work with images is the url-loader loader.

This example loads any PNG file smaller than 8KB as a data URL.

module.exports = {
  /*...*/
  module: {
    rules: [
      {
        test: /\.png$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192
            }
          }
        ]
      }
    ]
  }
  /*...*/
}

Process your SASS code and transform it to CSS

Using sass-loader, css-loader and style-loader:

module.exports = {
  /*...*/
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
      }
    ]
  }
  /*...*/
}

Generate Source Maps

Since webpack bundles the code, Source Maps are mandatory to get a reference to the original file that raised an error, for example.

You tell webpack to generate source maps using the devtool property of the configuration:

module.exports = {
  /*...*/
  devtool: 'inline-source-map',
  /*...*/
}

devtool has many possible values, the most used probably are:

  • none: adds no source maps
  • source-map: ideal for production, provides a separate source map that can be minimized, and adds a reference into the bundle, so development tools know that the source map is available. Of course you should configure the server to avoid shipping this, and just use it for debugging purposes
  • inline-source-map: ideal for development, inlines the source map as a Data URL

More devtools tutorials: