Vite · Part 9 — Plugins
The Rollup-compatible plugin API: the hooks that matter (resolveId, load, transform), enforce (pre/post) and apply (serve/build) ordering, and writing your own virtual-module plugin. With a plugin hook timeline.
Almost everything Vite does beyond plain JS/CSS goes through a plugin — frameworks, legacy support, PWA, SVG-as-component, and your own custom transforms {Hầu hết mọi thứ Vite làm ngoài JS/CSS thuần đều qua plugin — framework, hỗ trợ legacy, PWA, SVG-as-component, và biến đổi tùy chỉnh của bạn}. The good news: Vite plugins are Rollup plugins with a few extra hooks, so the knowledge transfers across the whole ecosystem {Tin tốt: plugin Vite là plugin Rollup với vài hook thêm, nên kiến thức chuyển giao khắp hệ sinh thái}.
Run a module through the plugin pipeline and watch each hook fire in order {Chạy một module qua pipeline plugin và xem mỗi hook kích hoạt theo thứ tự}:
1. A plugin is an object {Plugin là một object}
The minimal shape — a name and one or more hook functions {Hình dạng tối thiểu — một name và một hoặc nhiều hàm hook}:
import type { Plugin } from "vite";
function myPlugin(): Plugin {
return {
name: "my-plugin", // required, used in errors/warnings
transform(code, id) {
// rewrite a module's code
return null; // null = leave unchanged
},
};
}
// vite.config.ts
export default defineConfig({ plugins: [myPlugin()] });
You add it to plugins like any other {Bạn thêm vào plugins như bất kỳ cái nào khác}. Most plugins are functions returning this object so they can take options {Đa số plugin là hàm trả object này để nhận tùy chọn}.
2. The hooks that matter {Các hook quan trọng}
Vite runs hooks as it processes each module {Vite chạy hook khi xử lý từng module}. The ones you’ll actually use {Những cái bạn thực sự dùng}:
| Hook | When {Khi} | Use for {Dùng cho} |
|---|---|---|
config / configResolved | startup {khởi động} | read/tweak config {đọc/chỉnh config} |
configureServer | dev only {chỉ dev} | add dev-server middleware {thêm middleware dev-server} |
resolveId | per module {mỗi module} | claim/redirect an import id {nhận/chuyển hướng id import} |
load | per module {mỗi module} | provide source for an id {cấp source cho một id} |
transform | per module {mỗi module} | rewrite a module’s code {viết lại code module} |
handleHotUpdate | dev only {chỉ dev} | customize HMR behavior {tùy chỉnh hành vi HMR} |
renderChunk / generateBundle | build only {chỉ build} | edit output / emit files {sửa output / phát file} |
resolveId → load → transform is the core trio for every module {resolveId → load → transform là bộ ba cốt lõi cho mỗi module}. transform is the workhorse {transform là người làm chính}.
3. enforce and apply — ordering & scope {enforce và apply — thứ tự & phạm vi}
Plugin order can matter {Thứ tự plugin có thể quan trọng}. Vite runs plugins in three groups {Vite chạy plugin theo ba nhóm}:
function myPlugin(): Plugin {
return {
name: "my-plugin",
enforce: "pre", // "pre" → before core | undefined → with core | "post" → after
apply: "build", // "build" | "serve" | a function — limit when it runs
transform(code, id) {
/* ... */
},
};
}
enforce: "pre"— run before Vite’s core plugins (e.g. to transform source before it’s parsed) {chạy trước plugin core của Vite}.enforce: "post"— run after core (e.g. to post-process output) {chạy sau core}.apply: "serve" | "build"— only run in dev or only in build {chỉ chạy khi dev hoặc chỉ khi build}.
4. Writing a virtual-module plugin {Viết plugin virtual-module}
The most useful pattern to learn: a virtual module — an importable module that doesn’t exist on disk, whose source you generate at build time {Pattern hữu ích nhất để học: một virtual module — module import được nhưng không tồn tại trên đĩa, source do bạn sinh lúc build}:
function buildInfoPlugin(): Plugin {
const id = "virtual:build-info";
const resolvedId = "\0" + id; // the \0 prefix marks it virtual (convention)
return {
name: "build-info",
resolveId(source) {
if (source === id) return resolvedId; // "I own this import"
},
load(thisId) {
if (thisId === resolvedId) {
return `export const builtAt = ${JSON.stringify(new Date().toISOString())};`;
}
},
};
}
// anywhere in your app
import { builtAt } from "virtual:build-info";
resolveId claims the id; load returns the generated source {resolveId nhận id; load trả source được sinh}. The \0 prefix is the convention that tells Vite (and other plugins) “this isn’t a real file, don’t try to read it from disk” {Tiền tố \0 là quy ước nói với Vite (và plugin khác) “đây không phải file thật, đừng đọc từ đĩa”}. This is how vite/client, env injection, and many integrations work {Đây là cách vite/client, chèn env, và nhiều tích hợp hoạt động}.
5. A transform example {Ví dụ transform}
transform lets you rewrite real files {transform cho bạn viết lại file thật}. A toy plugin that replaces a token {Plugin đồ chơi thay một token}:
function bannerPlugin(): Plugin {
return {
name: "banner",
transform(code, id) {
if (!id.endsWith(".ts")) return null;
return { code: `/* generated ${Date.now()} */\n${code}`, map: null };
},
};
}
Return null to leave a module unchanged {Trả null để giữ module không đổi}; return { code, map } to rewrite it (provide a source map when you can) {trả { code, map } để viết lại (cung cấp source map khi có thể)}.
6. Compatibility note (Vite 8) {Lưu ý tương thích (Vite 8)}
Because Vite 8 uses Rolldown (which keeps the Rollup-compatible plugin API), the vast majority of existing Vite and Rollup plugins work unchanged {Vì Vite 8 dùng Rolldown (giữ API plugin tương thích Rollup), đại đa số plugin Vite và Rollup hiện có chạy không cần đổi}. When you write a plugin, target the documented Vite/Rollup hooks and you’re future-proof {Khi viết plugin, nhắm các hook Vite/Rollup được tài liệu hóa và bạn an toàn tương lai}.
7. Exercises {Bài tập}
1. Which trio of hooks handles a single module from import string to final code, and which is the “workhorse”? {Bộ ba hook nào xử lý một module từ chuỗi import tới code cuối, và cái nào là “người làm chính”?}
Solution {Lời giải}
resolveId → load → transform; transform is the workhorse that rewrites module code {resolveId → load → transform; transform là người làm chính viết lại code module}.
2. You want import config from "virtual:app-config" to work without any such file on disk. Which two hooks do you implement? {Bạn muốn import config from "virtual:app-config" chạy mà không có file đó trên đĩa. Hai hook nào?}
Solution {Lời giải}
resolveId (claim the id, return a \0-prefixed resolved id) and load (return the generated source) {resolveId (nhận id, trả id có tiền tố \0) và load (trả source sinh ra)}.
3. Your plugin must transform files before Vite’s core plugins see them, and only during dev. What do you set? {Plugin của bạn phải biến đổi file trước plugin core của Vite, và chỉ khi dev. Đặt gì?}
Solution {Lời giải}
enforce: "pre" and apply: "serve" {enforce: "pre" và apply: "serve"}.
Stretch {Nâng cao}: in the timeline, note which hooks are “dev only” vs “build only” — those are the ones gated by apply and the serve/build distinction {trong timeline, để ý hook nào “chỉ dev” vs “chỉ build” — đó là những cái bị giới hạn bởi apply và phân biệt serve/build}.
Key takeaways {Điểm chính}
- A Vite plugin is an object with a
nameand hooks — and it’s a Rollup plugin {Plugin Vite là object cónamevà hook — và là plugin Rollup}. resolveId→load→transformprocesses each module;transformis the workhorse {resolveId→load→transformxử lý mỗi module;transformlà người làm chính}.enforce(pre/post) controls order;apply(serve/build) controls when it runs {enforceđiều khiển thứ tự;applyđiều khiển khi nào chạy}.- Virtual modules (
resolveId+load,\0prefix) expose generated code as an import {Virtual module phơi code sinh ra dưới dạng import}. - Rolldown keeps the Rollup API, so most plugins just work {Rolldown giữ API Rollup, nên đa số plugin chạy luôn}.
Next up {Tiếp theo}
Part 10 — Production build with Rolldown: what vite build produces, chunk splitting and manualChunks, the new advancedChunks, asset handling, and reading the build output {Phần 10 — Build production với Rolldown: vite build tạo gì, tách chunk và manualChunks, advancedChunks mới, xử lý asset, và đọc output build}.