Vite ngày càng trở nên phổ biến hơn đối với các nhà phát triển, nhưng vì cộng đồng không lớn (như trong Webpack ), nên bạn có thể cần tạo plugin tùy chỉnh của riêng mình để giải quyết vấn đề của mình. Trong bài viết này, chúng tôi sẽ thảo luận về cách tạo plugin cho Vite và tôi sẽ chia nhỏ plugin của riêng mình.
Để tạo plugin, điều quan trọng cần biết là Vite sử dụng các hệ thống xây dựng khác nhau cho máy chủ phát triển (command vite
) và gói (command vite build
).
Đối với máy chủ phát triển, nó sử dụng esbuild với các mô-đun ES gốc, được hỗ trợ bởi các trình duyệt hiện đại và chúng tôi không cần phải gộp mã vào một tệp duy nhất và nó cung cấp cho chúng tôi HRM (Thay thế mô-đun nóng) nhanh chóng.
Đối với gói, nó sử dụng rollup.js vì nó linh hoạt và có hệ sinh thái rộng lớn; nó cho phép tạo ra các gói sản xuất được tối ưu hóa cao với các định dạng đầu ra khác nhau.
Giao diện plugin của Vite dựa trên Rollup nhưng có các tùy chọn và hook bổ sung để làm việc với máy chủ nhà phát triển.
Khi tạo một plugin, bạn có thể đưa plugin đó vào vite.config.js
của mình. Không cần phải tạo một gói mới cho nó. Khi bạn thấy rằng một plugin hữu ích trong các dự án của mình, hãy cân nhắc việc chia sẻ plugin đó với cộng đồng và đóng góp cho hệ sinh thái Vite.
Ngoài ra, vì rollup.js có cộng đồng và hệ sinh thái lớn hơn nên bạn có thể cân nhắc việc tạo một plugin cho rollup.js và nó sẽ hoạt động tốt như vậy trong Vite. Vì vậy, nếu chức năng plugin của bạn chỉ hoạt động với gói, bạn có thể sử dụng plugin rollup.js thay vì Vite và người dùng có thể sử dụng plugin rollup của bạn trong các dự án Vite của họ mà không gặp vấn đề gì.
Nếu bạn tạo một plugin cho rollup, bạn sẽ thu hút được nhiều người dùng chỉ sử dụng rollup.js hơn. Nếu plugin của bạn ảnh hưởng đến máy chủ phát triển thì bạn hãy sử dụng plugin Vite.
Hãy bắt đầu tạo plugin trực tiếp trong vite.config.ts
:
// vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin { return { name: 'my-plugin', configResolved(config) { console.log(config); }, }; } export default defineConfig({ plugins: [ myPlugin(), ], });
Trong ví dụ này, tôi đã tạo một plugin có tên myPlugin
để in cấu hình Vite ngay khi nó được giải quyết trong bảng điều khiển ở cả hai giai đoạn: máy chủ dev và gói. Nếu tôi chỉ muốn in cấu hình ở chế độ máy chủ nhà phát triển thì tôi nên thêm apply: 'serve'
và apply: 'build'
cho gói.
// vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin { return { name: 'my-plugin', apply: 'serve', configResolved(config) { console.log(config); }, }; } export default defineConfig({ plugins: [ myPlugin(), ], });
Ngoài ra, tôi có thể trả về một loạt plugin; nó rất hữu ích trong việc tách chức năng cho máy chủ dev và gói:
// vite.config.ts import { defineConfig, Plugin } from 'vite'; function myPlugin(): Plugin[] { return [ { name: 'my-plugin:serve', apply: 'serve', configResolved(config) { console.log('dev server:', config); }, }, { name: 'my-plugin:build', apply: 'build', configResolved(config) { console.log('bundle:', config); }, }, ]; } export default defineConfig({ plugins: [ myPlugin(), ], });
Và đó là khá nhiều; bạn có thể dễ dàng thêm các plugin nhỏ vào cấu hình Vite. Và nếu plugin quá lớn, tôi thích chuyển nó sang tệp khác hoặc thậm chí tạo một gói.
Nếu bạn cần thứ gì đó phức tạp hơn, có rất nhiều hook hữu ích mà bạn có thể khám phá trong tài liệu của Vite. Nhưng để làm ví dụ, hãy chia nhỏ plugin của riêng tôi bên dưới.
Vì vậy, tôi có một plugin để tạo các họa tiết SVG dựa trên các tệp biểu tượng - vite-plugin-svg-spritemap .
Mục đích là lấy tất cả các biểu tượng .svg
trong thư mục src/icons
và thu thập nội dung của chúng vào một tệp .svg
duy nhất được gọi là SVG sprite. Hãy bắt đầu từ giai đoạn gói:
import { Plugin, ResolvedConfig } from 'vite'; import path from 'path'; import fs from 'fs-extra'; function myPlugin(): Plugin { let config: ResolvedConfig; return { name: 'my-plugin:build', apply: 'build', async configResolved(_config) { config = _config; }, writeBundle() { const sprite = getSpriteContent({ pattern: 'src/icons/*.svg' }); const filePath = path.resolve(config.root, config.build.outDir, 'sprite.svg'); fs.ensureFileSync(filePath); fs.writeFileSync(filePath, sprite); }, }; }
Hook configResolved
cho phép chúng ta lấy cấu hình khi quyết định sử dụng nó trong các hook tiếp theo;
Hook writeBundle
được gọi sau khi quá trình đóng gói kết thúc và ở đây, tôi sẽ tạo tệp sprite.svg
;
Hàm getSpriteContent
trả về một chuỗi các họa tiết SVG đã chuẩn bị dựa trên mẫu src/icons/*.svg
. Tôi sẽ không đi sâu hơn với điều này; bạn có thể xem bài viết khác của tôi giải thích toàn bộ quá trình tạo SVG sprite ;
Sau đó, tôi tạo đường dẫn tuyệt đối đến sprite.svg
để đưa nội dung sprite SVG vào path.resolve()
, đảm bảo rằng tệp đó tồn tại (hoặc tạo một) với fs.ensureFileSync
., và ghi nội dung sprite SVG vào đó .
Bây giờ là phần thú vị nhất - giai đoạn máy chủ phát triển. Tôi không thể sử dụng writeBundle
ở đây và tôi không thể lưu trữ tệp khi máy chủ nhà phát triển đang chạy, vì vậy chúng tôi cần sử dụng phần mềm trung gian của máy chủ để nhận yêu cầu tới sprite.svg
.
import { Plugin, ResolvedConfig } from 'vite'; function myPlugin(): Plugin { let config: ResolvedConfig; return { name: `my-plugin:serve`, apply: 'serve', async configResolved(_config) { config = _config; }, configureServer(server) { // (1) return () => { server.middlewares.use(async (req, res, next) => { // (2) if (req.url !== '/sprite.svg') { return next(); // (3) } const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor }); res.writeHead(200, { // (4) 'Content-Type': 'image/svg+xml, charset=utf-8', 'Cache-Control': 'no-cache', }); res.end(sprite); }); }; }, }; }
configureServer
là một cái móc để định cấu hình máy chủ dev. Nó kích hoạt trước khi cài đặt phần mềm trung gian nội bộ của Vite; trong trường hợp của tôi, tôi cần thêm phần mềm trung gian tùy chỉnh sau phần mềm trung gian nội bộ, vì vậy tôi trả về một hàm;
Để thêm phần mềm trung gian tùy chỉnh nhằm đáp ứng mọi yêu cầu đến máy chủ nhà phát triển, tôi sử dụng server.middlewares.use()
. Tôi cần nó để phát hiện các yêu cầu có URL [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg)
, để tôi có thể mô phỏng hành vi của tệp;
Nếu URL yêu cầu không phải là /sprite.svg
- hãy chuyển sang phần mềm trung gian tiếp theo (tức là chuyển quyền kiểm soát cho trình xử lý tiếp theo trong chuỗi);
Để chuẩn bị nội dung tệp, tôi đặt kết quả của getSpriteContent
vào biến sprite
và gửi nó dưới dạng phản hồi với các tiêu đề được định cấu hình (loại nội dung và trạng thái 200 HTTP).
Kết quả là tôi đã mô phỏng hành vi của tệp.
Nhưng nếu các tập tin trong src/icons
bị thay đổi, xóa hoặc thêm vào, chúng ta nên khởi động lại máy chủ để tạo nội dung sprite mới thông qua getSpriteContent
; để làm điều này, tôi sẽ sử dụng thư viện xem tệp - chokidar . Hãy thêm trình xử lý chokidar vào mã:
import { Plugin, ResolvedConfig } from 'vite'; import chokidar from 'chokidar'; function myPlugin(): Plugin { let config: ResolvedConfig; let watcher: chokidar.FSWatcher; // Defined variable for chokidar instance. return { name: `my-plugin:serve`, apply: 'serve', async configResolved(_config) { config = _config; }, configureServer(server) { function reloadPage() { // Function that sends a signal to reload the server. server.ws.send({ type: 'full-reload', path: '*' }); } watcher = chokidar .watch('src/icons/*.svg', { // Watch src/icons/*.svg cwd: config.root, // Define project root path ignoreInitial: true, // Don't trigger chokidar on instantiation. }) .on('add', reloadPage) // Add listeners to add, modify, delete. .on('change', reloadPage) .on('unlink', reloadPage); return () => { server.middlewares.use(async (req, res, next) => { if (req.url !== '/sprite.svg') { return next(); } const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor }); res.writeHead(200, { 'Content-Type': 'image/svg+xml, charset=utf-8', 'Cache-Control': 'no-cache', }); res.end(sprite); }); }; }, }; }
Như bạn có thể thấy, API tạo plugin không thực sự phức tạp. Bạn chỉ cần tìm những hook từ Vite hoặc Rollup phù hợp với nhiệm vụ của mình. Trong ví dụ của tôi, tôi đang sử dụng writeBundle
từ Rollup.js (như tôi đã nói, nó được sử dụng để tạo gói) và configureServer
từ Vite vì Rollup.js không có hỗ trợ máy chủ nhà phát triển gốc.
Trong trường hợp writeBundle
, nó rất đơn giản, chúng tôi lấy nội dung SVG sprite và đặt nó vào một tệp. Trong trường hợp của máy chủ dev, tôi bối rối tại sao tôi không thể làm như vậy; Tôi đã xem xét các plugin của các tác giả khác và tất cả họ đều làm như vậy.
Vì vậy, tôi sử dụng configureServer
và thông qua đối số server
, tôi thêm phần mềm trung gian kích hoạt mọi yêu cầu đến máy chủ nhà phát triển bằng cách chặn yêu cầu sprite.svg
.
Như tôi đã đề cập trước đó, để tạo ra nhiều plugin hữu ích hơn, bạn cần khám phá các hook. Chúng được giải thích chi tiết trong tài liệu:
https://vitejs.dev/guide/api-plugin#universal-hooks
https://vitejs.dev/guide/api-plugin#vite-spec-hooks
Về cách đặt tên, Vite có một số quy ước dành cho plugin, vì vậy tốt nhất bạn nên kiểm tra nó trước khi hoàn thành. Dưới đây là một số điểm chính:
vite-plugin-
;
vite-plugin
trong pack.json;
vite-plugin-vue-
, vite-plugin-react-
, vite-plugin-svelte-
).Nếu bạn quyết định xuất bản plugin của mình trong NPM, tôi khuyên bạn nên làm điều này vì chia sẻ kiến thức và kiến thức chuyên môn là nguyên tắc cơ bản của cộng đồng CNTT, thúc đẩy sự phát triển chung. Để tìm hiểu cách xuất bản và duy trì gói của bạn, hãy xem hướng dẫn của tôi → Cách dễ nhất để tạo gói NPM .
Tôi cũng thực sự khuyên bạn nên gửi plugin của mình tới danh sách cộng đồng của vite - awesome-vite . Nhiều người đang tìm kiếm các plugin phù hợp nhất ở đó và đây sẽ là cơ hội tuyệt vời để đóng góp cho hệ sinh thái Vite! Quá trình gửi plugin của bạn ở đó rất đơn giản - chỉ cần đảm bảo bạn đáp ứng các điều khoản và tạo yêu cầu kéo. Bạn có thể tìm thấy danh sách các điều khoản ở đây .
Nhìn chung, nó cần phải dành riêng cho Vite (không phải bản tổng hợp), nguồn mở và có tài liệu tốt. Chúc may mắn với các plugin của bạn!