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.

Loaders work on one file at a time {Loader làm việc trên một file mỗi lần}. Plugins are far more powerful: they tap into the entire compilation lifecycle and can do anything — generate HTML, extract CSS, define globals, copy files, inject banners {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}. If loaders are about transforming, plugins are about everything else {Nếu loader là về biến đổi, thì plugin là về mọi thứ còn lại}.

Toggle plugins below, run the build, and watch each fire at its hook {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. How plugins work {Plugin hoạt động thế nào}

A plugin is a class with an apply(compiler) method {Plugin là một class có method apply(compiler)}. Inside, it taps into one or more hooks that Webpack fires at specific points in the build {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");
    });
  }
}

Webpack’s whole architecture is built on the tapable hook system — compile, make, emit, afterEmit, done, and many more, in a fixed order {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}. The demo above shows the major ones and which plugin taps each {Demo trên hiện các hook chính và plugin nào tap cái nào}. We write our own plugin in Part 10 {Ta tự viết plugin ở Phần 10}.


2. Using plugins {Dùng plugin}

Plugins go in the plugins array — instances, created with new {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") }),
  ],
};

The most common beginner mistake is forgetting new {Lỗi người mới phổ biến nhất là quên new}.


3. HtmlWebpackPlugin — the must-have {HtmlWebpackPlugin — cái phải có}

It generates an index.html and automatically injects <script> (and <link>) tags for your bundles — crucial once filenames have content hashes {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",
})

Without it you’d hand-edit <script src="main.abc123.js"> after every build — impossible with hashed names {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 — compile-time constants {DefinePlugin — hằng số lúc biên dịch}

It replaces expressions with values at build time, before minification {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),
})

This is how process.env.NODE_ENV === "production" checks get eliminated, letting tree shaking drop dev-only code (Part 7) {Đâ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)}. Note the JSON.stringify — values are inlined as literal code, so a string needs its quotes {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 — real CSS files {MiniCssExtractPlugin — file CSS thật}

In Part 3 style-loader injected CSS via JS at runtime {Ở Phần 3 style-loader tiêm CSS qua JS lúc chạy}. For production you want a separate cacheable .css file instead {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" })],
};

Notice it’s both a loader (replacing style-loader) and a plugin (to emit the file) — a common pattern {Để ý nó vừa là loader (thay style-loader) vừa là plugin (để phát file) — mẫu phổ biến}.


6. Other plugins you’ll meet {Plugin khác bạn sẽ gặp}

PluginDoes {Làm}
CopyWebpackPlugincopies static files (favicon, robots.txt) into dist/ {copy file tĩnh vào dist/}
BannerPlugin (built-in)prepends a license/comment banner {thêm banner license/comment}
ProgressPlugin (built-in)reports build progress {báo tiến độ build}
BundleAnalyzerPluginvisualizes bundle size (Part 9) {trực quan kích thước bundle (Phần 9)}
DefinePlugin / EnvironmentPlugininject build-time constants {tiêm hằng số lúc build}

Built-in plugins live on the webpack object (new webpack.BannerPlugin(...)); others are separate npm packages {Plugin tích hợp nằm trên object webpack; cái khác là package npm riêng}.


7. Loaders vs plugins {Loader vs plugin}

The distinction in one line {Phân biệt trong một dòng}: loaders transform individual files as they enter the graph; plugins act on the whole build and its output {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ó}. Need to handle a file type? Loader {Cần xử lý một loại file? Loader}. Need to generate, copy, inject, or optimize across the build? Plugin {Cần tạo, copy, tiêm, hay tối ưu xuyên build? Plugin}.


8. Exercises {Bài tập}

1. You add HtmlWebpackPlugin but Webpack throws “is not a constructor”. What did you forget? {Bạn thêm HtmlWebpackPlugin nhưng Webpack ném “is not a constructor”. Bạn quên gì?}

Solution {Lời giải}

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

2. DefinePlugin injects your API URL but the build fails with a syntax error where the value is used. Why? {DefinePlugin tiêm URL API nhưng build lỗi cú pháp ở nơi dùng giá trị. Vì sao?}

Solution {Lời giải}

You passed the raw string instead of JSON.stringify(...), so it was inlined as an identifier, not a string literal {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. You want a cacheable styles.css file in production instead of JS-injected styles. Which plugin + loader change? {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?}

Solution {Lời giải}

Use MiniCssExtractPlugin and replace style-loader with MiniCssExtractPlugin.loader {Dùng MiniCssExtractPlugin và thay style-loader bằng MiniCssExtractPlugin.loader}.

Stretch {Nâng cao}: in the visualizer, enable MiniCssExtractPlugin and CopyWebpackPlugin, run the build, and note the extra main.css, favicon.ico, and robots.txt in the output {trong trình trực quan, bật MiniCssExtractPlugin và CopyWebpackPlugin, chạy build, và để ý thêm main.css, favicon.ico, robots.txt ở output}.


Key takeaways {Điểm chính}

  • Plugins tap into build hooks (compile, emit, done…) and act on the whole compilation {Plugin tap vào hook build và tác động lên cả quá trình biên dịch}.
  • They go in the plugins array as instances (new) {Chúng nằm trong mảng plugins dưới dạng instance (new)}.
  • HtmlWebpackPlugin generates HTML and injects hashed bundle tags {HtmlWebpackPlugin tạo HTML và tiêm thẻ bundle đã băm}.
  • DefinePlugin inlines compile-time constants (use JSON.stringify) {DefinePlugin nội tuyến hằng số lúc biên dịch}.
  • MiniCssExtractPlugin emits real .css files for production {MiniCssExtractPlugin phát file .css thật cho production}.

Next up {Tiếp theo}

Part 5 — Dev experience: webpack-dev-server, Hot Module Replacement, and source maps — the fast feedback loop that makes day-to-day development pleasant {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}.