Neovim · Part 5 — Plugin architecture với lazy.nvim
Cài lazy.nvim, chia plugin specs, hiểu lazy-loading, dependencies, opts/config, lockfile, update và cách không biến config thành mớ rối.
Plugin manager là bước biến Neovim từ editor tối giản thành môi trường làm việc hiện đại.
Năm 2025-2026 có hai hướng đáng biết:
vim.pack: package manager built-in mới của Neovim, nhưng vẫn được ghi là experimental.lazy.nvim: plugin manager thực dụng, ổn định, có UI, lockfile, lazy-loading, profile.
Series này dùng lazy.nvim vì người chuyển từ VSCode cần một hệ sinh thái dễ học, dễ debug.
Bootstrap lazy.nvim
init.lua:
require("config.options")
require("config.keymaps")
require("config.autocmds")
require("config.lazy")
Tạo lua/config/lazy.lua:
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"--branch=stable",
lazyrepo,
lazypath,
})
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
require("lazy").setup({
spec = {
{ import = "plugins" },
},
install = { colorscheme = { "habamax" } },
checker = { enabled = true },
change_detection = { notify = false },
})
Tạo thư mục:
lua/
├─ config/
│ └─ lazy.lua
└─ plugins/
├─ ui.lua
├─ editor.lua
├─ lsp.lua
└─ coding.lua
Plugin spec là gì?
Một plugin spec là một table Lua mô tả plugin:
return {
{
"folke/which-key.nvim",
event = "VeryLazy",
opts = {},
},
}
Các field thường gặp:
| Field | Nghĩa |
|---|---|
| string đầu | repo GitHub |
event | load khi event xảy ra |
cmd | load khi gọi command |
keys | load khi bấm key |
ft | load theo filetype |
dependencies | plugin phụ thuộc |
opts | options truyền vào setup() |
config | function tự cấu hình |
build | lệnh chạy sau install/update |
version | pin semver/tag |
Quy tắc: dùng opts khi plugin có setup đơn giản; dùng config khi cần logic.
Plugin đầu tiên: which-key
Tạo lua/plugins/editor.lua:
return {
{
"folke/which-key.nvim",
event = "VeryLazy",
opts = {
preset = "modern",
},
},
}
Mở Nvim, chạy:
:Lazy
lazy.nvim sẽ cài plugin thiếu. Sau đó bấm <leader> và chờ một chút, which-key sẽ hiện các keymap có desc.
Theme và UI tối thiểu
Tạo lua/plugins/ui.lua:
return {
{
"folke/tokyonight.nvim",
lazy = false,
priority = 1000,
opts = {
style = "night",
},
config = function(_, opts)
require("tokyonight").setup(opts)
vim.cmd.colorscheme("tokyonight")
end,
},
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = {
options = {
theme = "auto",
globalstatus = true,
},
},
},
}
Theme dùng lazy = false và priority cao để load sớm. Statusline có thể load trễ.
Lazy-loading không phải trò ảo thuật
Lazy-loading tốt khi plugin không cần chạy ngay. Nhưng đừng lazy-load mọi thứ.
| Plugin | Có nên lazy-load? | Lý do |
|---|---|---|
| colorscheme | Không | cần trước khi UI render |
| mason.nvim | Không nên defer | setup PATH/tooling sớm |
| Treesitter parser layer | Cẩn thận | nhiều thay đổi, không nên load quá muộn |
| picker | Có | chỉ cần khi tìm file/grep |
| git signs | Có thể theo BufReadPre | chỉ cần khi mở file |
| which-key | Có | UI helper |
Nếu plugin không hoạt động, thử bỏ lazy-loading trước khi debug sâu.
Lockfile
lazy.nvim tạo:
lazy-lock.json
Commit file này vào Git. Nó giúp máy khác cài đúng revision plugin.
Workflow update:
:Lazy
Sau đó:
- bấm
Uđể update; - đọc diff trong UI;
- restart Nvim;
- nếu hỏng, revert
lazy-lock.json.
Đừng update plugin trong lúc deadline trừ khi bạn thích cảm giác tim đập nhanh.
Commands nên biết
| Lệnh | Ý nghĩa |
|---|---|
:Lazy | mở UI |
:Lazy sync | install/update/clean |
:Lazy update | update plugin |
:Lazy clean | xóa plugin không còn dùng |
:Lazy profile | xem startup/plugin cost |
:Lazy health | kiểm tra lazy.nvim |
Anti-pattern
Đừng làm thế này:
return {
"plugin/a",
"plugin/b",
"plugin/c",
"plugin/d",
}
Không event, không opts, không desc, không lý do. Sau 3 tháng bạn sẽ không biết plugin nào làm gì.
Nên làm:
return {
{
"lewis6991/gitsigns.nvim",
event = { "BufReadPre", "BufNewFile" },
opts = {},
},
}
Mỗi plugin nên có:
- điều kiện load rõ;
- config tối thiểu;
- keymap có
desc; - lý do tồn tại.
Bài tập
- Cài
lazy.nvimtheo cấu trúc trên. - Thêm
which-key.nvim. - Thêm một colorscheme bạn thích.
- Chạy
:Lazy profile, ghi lại startup time. - Commit
lazy-lock.jsonvào dotfiles.
Checklist lỗi thường gặp
module 'lazy' not found: kiểm travim.opt.rtp:prepend(lazypath).- Plugin không cài: kiểm tra Git/network.
- Keymap không hiện trong which-key: thiếu
deschoặc plugin chưa load. - Theme chớp màu khi mở: colorscheme bị lazy-load quá muộn.
Điều cốt lõi
lazy.nvim không chỉ để cài plugin. Nó là kiến trúc config: chia module, kiểm soát load, giữ lockfile, update có kiểm soát. Từ đây Neovim bắt đầu thành editor của bạn.