jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Webpack · Part 4 — Plugins

Where loaders transform individual files, plugins hook into the whole build — HtmlWebpackPlugin, DefinePlugin, MiniCssExtractPlugin, CopyWebpackPlugin — and how the tapable hook system works. With an interactive plugin lifecycle visualizer.

Loader làm việc trên một file mỗi lần. Plugin mạnh hơn nhiều: chúng móc vào toàn bộ vòng đời biên dịch và làm được mọi thứ — tạo HTML, trích CSS, định nghĩa global, copy file, tiêm banner. Nếu loader là về biến đổi, thì plugin là về mọi thứ còn lại.

Bật/tắt plugin bên dưới, chạy build, và xem mỗi cái kích hoạt ở hook của nó:


1. Plugin hoạt động thế nào

Plugin là một class có method apply(compiler). Bên trong, nó tap vào một hoặc nhiều hook mà Webpack kích hoạt ở các điểm cụ thể trong build:

class MyPlugin {
  apply(compiler) {
    compiler.hooks.done.tap("MyPlugin", (stats) => {
      console.log("Build finished in", stats.endTime - stats.startTime, "ms");
    });
  }
}

Toàn bộ kiến trúc Webpack dựng trên hệ thống hook tapablecompile, make, emit, afterEmit, done, và nhiều nữa, theo thứ tự cố định. Demo trên hiện các hook chính và plugin nào tap cái nào. Ta tự viết plugin ở Phần 10.


2. Dùng plugin

Plugin nằm trong mảng plugins — là instance, tạo bằng new:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({ template: "./src/index.html" }),
    new webpack.DefinePlugin({ "process.env.API": JSON.stringify("https://api.example.com") }),
  ],
};

Lỗi người mới phổ biến nhất là quên new.


3. HtmlWebpackPlugin — cái phải có

Nó tạo index.htmltự động tiêm thẻ <script> (và <link>) cho bundle — cực kỳ quan trọng một khi filename có content hash:

npm install --save-dev html-webpack-plugin
new HtmlWebpackPlugin({
  template: "./src/index.html", // your HTML shell
  title: "My App",
  favicon: "./src/favicon.ico",
})

Không có nó bạn phải sửa tay <script src="main.abc123.js"> sau mỗi build — bất khả với tên băm.


4. DefinePlugin — hằng số lúc biên dịch

Nó thay biểu thức bằng giá trị lúc build, trước khi minify:

new webpack.DefinePlugin({
  "process.env.NODE_ENV": JSON.stringify("production"),
  __VERSION__: JSON.stringify(require("./package.json").version),
})

Đây là cách các kiểm tra process.env.NODE_ENV === "production" bị loại bỏ, để tree shaking bỏ code chỉ-dành-cho-dev (Phần 7). Lưu ý JSON.stringify — giá trị được nội tuyến như code literal, nên chuỗi cần dấu nháy.


5. MiniCssExtractPlugin — file CSS thật

Ở Phần 3 style-loader tiêm CSS qua JS lúc chạy. Cho production bạn muốn một file .css riêng, cache được:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  module: {
    rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] }],
  },
  plugins: [new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" })],
};

Để ý nó vừa là loader (thay style-loader) vừa là plugin (để phát file) — mẫu phổ biến.


6. Plugin khác bạn sẽ gặp

PluginLàm
CopyWebpackPlugincopy file tĩnh vào dist/
BannerPlugin (built-in)thêm banner license/comment
ProgressPlugin (built-in)báo tiến độ build
BundleAnalyzerPlugintrực quan kích thước bundle (Phần 9)
DefinePlugin / EnvironmentPlugintiêm hằng số lúc build

Plugin tích hợp nằm trên object webpack; cái khác là package npm riêng.


7. Loader vs plugin

Phân biệt trong một dòng: loader biến đổi từng file khi chúng vào đồ thị; plugin tác động lên cả build và output của nó. Cần xử lý một loại file? Loader. Cần tạo, copy, tiêm, hay tối ưu xuyên build? Plugin.


8. Bài tập

1. Bạn thêm HtmlWebpackPlugin nhưng Webpack ném “is not a constructor”. Bạn quên gì?

Lời giải

new — plugin phải được khởi tạo: new HtmlWebpackPlugin({...}).

2. DefinePlugin tiêm URL API nhưng build lỗi cú pháp ở nơi dùng giá trị. Vì sao?

Lời giải

Bạn truyền chuỗi thô thay vì JSON.stringify(...), nên nó bị nội tuyến như định danh, không phải string literal.

3. Bạn muốn một file styles.css cache được ở production thay vì style tiêm bằng JS. Đổi plugin + loader nào?

Lời giải

Dùng MiniCssExtractPlugin và thay style-loader bằng MiniCssExtractPlugin.loader.

Nâng cao:trong trình trực quan, bật MiniCssExtractPlugin và CopyWebpackPlugin, chạy build, và để ý thêm main.css, favicon.ico, robots.txt ở output.


Điểm chính

  • Plugin tap vào hook build và tác động lên cả quá trình biên dịch.
  • Chúng nằm trong mảng plugins dưới dạng instance (new).
  • HtmlWebpackPlugin tạo HTML và tiêm thẻ bundle đã băm.
  • DefinePlugin nội tuyến hằng số lúc biên dịch.
  • MiniCssExtractPlugin phát file .css thật cho production.

Tiếp theo

Phần 5 — Trải nghiệm dev: webpack-dev-server, Hot Module Replacement, và source map — vòng phản hồi nhanh khiến phát triển hằng ngày dễ chịu.