معماری پلاگین Vite را کاوش کرده و ساخت پلاگینهای سفارشی برای بهبود گردش کار توسعه خود را بیاموزید. بر مفاهیم کلیدی با مثالهای عملی برای مخاطبان جهانی مسلط شوید.
رمزگشایی از معماری پلاگین Vite: راهنمای جهانی برای ساخت پلاگینهای سفارشی
Vite، ابزار ساخت سریع و برقآسا، انقلابی در توسعه فرانتاند ایجاد کرده است. سرعت و سادگی آن عمدتاً به دلیل معماری پلاگین قدرتمند آن است. این معماری به توسعهدهندگان اجازه میدهد تا عملکرد Vite را گسترش داده و آن را متناسب با نیازهای خاص پروژه خود سفارشی کنند. این راهنما یک کاوش جامع در سیستم پلاگین Vite ارائه میدهد و شما را قادر میسازد تا پلاگینهای سفارشی خود را ایجاد کرده و گردش کار توسعه خود را بهینه کنید.
درک اصول اصلی Vite
قبل از پرداختن به ساخت پلاگین، درک اصول بنیادین Vite ضروری است:
- کامپایل بر اساس تقاضا (On-Demand Compilation): Vite فقط زمانی کد را کامپایل میکند که توسط مرورگر درخواست شود، که به طور قابل توجهی زمان راهاندازی را کاهش میدهد.
- ESM نیتیو: Vite از ماژولهای ECMAScript (ESM) نیتیو برای توسعه استفاده میکند، که نیاز به باندل کردن در حین توسعه را از بین میبرد.
- ساخت پروداکشن مبتنی بر Rollup: برای ساختهای پروداکشن، Vite از Rollup، یک باندلر بسیار بهینهسازی شده، برای تولید کدی کارآمد و آماده برای پروداکشن استفاده میکند.
نقش پلاگینها در اکوسیستم Vite
معماری پلاگین Vite به گونهای طراحی شده است که بسیار توسعهپذیر باشد. پلاگینها میتوانند:
- کد را تغییر دهند (مثلاً، کامپایل TypeScript، افزودن پیشپردازندهها).
- فایلهای سفارشی را سرو کنند (مثلاً، مدیریت داراییهای استاتیک، ایجاد ماژولهای مجازی).
- فرایند ساخت را اصلاح کنند (مثلاً، بهینهسازی تصاویر، تولید سرویس ورکرها).
- رابط خط فرمان (CLI) Vite را گسترش دهند (مثلاً، افزودن دستورات سفارشی).
پلاگینها کلید تطبیق Vite با نیازمندیهای مختلف پروژه، از تغییرات ساده تا یکپارچهسازیهای پیچیده، هستند.
معماری پلاگین Vite: یک بررسی عمیق
یک پلاگین Vite اساساً یک شیء جاوا اسکریپت با ویژگیهای خاص است که رفتار آن را تعریف میکند. بیایید عناصر کلیدی را بررسی کنیم:
پیکربندی پلاگین
فایل `vite.config.js` (یا `vite.config.ts`) جایی است که شما پروژه Vite خود را پیکربندی میکنید، از جمله مشخص کردن پلاگینهایی که باید استفاده شوند. گزینه `plugins` یک آرایه از اشیاء پلاگین یا توابعی که اشیاء پلاگین را برمیگردانند، میپذیرد.
// vite.config.js
import myPlugin from './my-plugin';
export default {
plugins: [
myPlugin(), // فراخوانی تابع پلاگین برای ایجاد یک نمونه از پلاگین
],
};
ویژگیهای شیء پلاگین
یک شیء پلاگین Vite میتواند چندین ویژگی داشته باشد که رفتار آن را در مراحل مختلف فرایند ساخت تعریف میکند. در اینجا تفکیکی از رایجترین ویژگیها آورده شده است:
- name: یک نام منحصربهفرد برای پلاگین. این مورد الزامی است و به اشکالزدایی و حل تداخلها کمک میکند. مثال: `'my-custom-plugin'`
- enforce: ترتیب اجرای پلاگین را تعیین میکند. مقادیر ممکن `'pre'` (قبل از پلاگینهای اصلی اجرا میشود)، `'normal'` (پیشفرض)، و `'post'` (بعد از پلاگینهای اصلی اجرا میشود) هستند. مثال: `'pre'`
- config: امکان تغییر شیء پیکربندی Vite را فراهم میکند. این تابع پیکربندی کاربر و محیط (mode و command) را دریافت میکند. مثال: `config: (config, { mode, command }) => { ... }`
- configResolved: پس از اینکه پیکربندی Vite به طور کامل حل شد، فراخوانی میشود. برای دسترسی به شیء پیکربندی نهایی مفید است. مثال: `configResolved(config) { ... }`
- configureServer: دسترسی به نمونه سرور توسعه (شبیه Connect/Express) را فراهم میکند. برای افزودن میانافزار سفارشی یا تغییر رفتار سرور مفید است. مثال: `configureServer(server) { ... }`
- transformIndexHtml: امکان تغییر فایل `index.html` را فراهم میکند. برای تزریق اسکریپتها، استایلها یا متا تگها مفید است. مثال: `transformIndexHtml(html) { ... }`
- resolveId: امکان رهگیری و تغییر در فرآیند تحلیل ماژولها را فراهم میکند. برای منطق تحلیل ماژول سفارشی مفید است. مثال: `resolveId(source, importer) { ... }`
- load: امکان بارگذاری ماژولهای سفارشی یا تغییر محتوای ماژولهای موجود را فراهم میکند. برای ماژولهای مجازی یا لودرهای سفارشی مفید است. مثال: `load(id) { ... }`
- transform: کد منبع ماژولها را تغییر میدهد. شبیه به یک پلاگین Babel یا PostCSS. مثال: `transform(code, id) { ... }`
- buildStart: در ابتدای فرایند ساخت فراخوانی میشود. مثال: `buildStart() { ... }`
- buildEnd: پس از اتمام فرایند ساخت فراخوانی میشود. مثال: `buildEnd() { ... }`
- closeBundle: پس از اینکه باندل روی دیسک نوشته شد، فراخوانی میشود. مثال: `closeBundle() { ... }`
- writeBundle: قبل از نوشتن باندل روی دیسک فراخوانی میشود و امکان تغییر را فراهم میکند. مثال: `writeBundle(options, bundle) { ... }`
- renderError: امکان رندر صفحات خطای سفارشی در حین توسعه را فراهم میکند. مثال: `renderError(error, req, res) { ... }`
- handleHotUpdate: امکان کنترل دقیق بر روی HMR را فراهم میکند. مثال: `handleHotUpdate({ file, server }) { ... }`
هوکهای پلاگین و ترتیب اجرا
پلاگینهای Vite از طریق یک سری هوکها عمل میکنند که در مراحل مختلف فرایند ساخت فعال میشوند. درک ترتیب اجرای این هوکها برای نوشتن پلاگینهای مؤثر حیاتی است.
- config: تغییر پیکربندی Vite.
- configResolved: دسترسی به پیکربندی حلشده.
- configureServer: تغییر سرور توسعه (فقط در حین توسعه).
- transformIndexHtml: تغییر فایل `index.html`.
- buildStart: شروع فرایند ساخت.
- resolveId: تحلیل شناسههای ماژول.
- load: بارگذاری محتوای ماژول.
- transform: تغییر کد ماژول.
- handleHotUpdate: مدیریت جایگزینی ماژول داغ (HMR).
- writeBundle: تغییر باندل خروجی قبل از نوشتن روی دیسک.
- closeBundle: پس از نوشته شدن باندل خروجی روی دیسک فراخوانی میشود.
- buildEnd: پایان فرایند ساخت.
ایجاد اولین پلاگین سفارشی Vite
بیایید یک پلاگین ساده Vite ایجاد کنیم که یک بنر به بالای هر فایل جاوا اسکریپت در ساخت پروداکشن اضافه میکند. این بنر شامل نام و نسخه پروژه خواهد بود.
پیادهسازی پلاگین
// banner-plugin.js
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
export default function bannerPlugin() {
return {
name: 'banner-plugin',
apply: 'build',
transform(code, id) {
if (!id.endsWith('.js')) {
return code;
}
const packageJsonPath = resolve(process.cwd(), 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
const banner = `/**\n * Project: ${packageJson.name}\n * Version: ${packageJson.version}\n */\n`;
return banner + code;
},
};
}
توضیحات:
- name: نام پلاگین را 'banner-plugin' تعریف میکند.
- apply: مشخص میکند که این پلاگین فقط باید در طول فرایند ساخت اجرا شود. تنظیم این مقدار به 'build' آن را فقط برای پروداکشن میکند و از سربار غیرضروری در حین توسعه جلوگیری میکند.
- transform(code, id):
- این هسته پلاگین است. این تابع کد (`code`) و شناسه (`id`) هر ماژول را رهگیری میکند.
- بررسی شرطی: `if (!id.endsWith('.js'))` تضمین میکند که این تغییر فقط روی فایلهای جاوا اسکریپت اعمال شود. این کار از پردازش انواع دیگر فایلها (مانند CSS یا HTML) که میتواند باعث خطا یا رفتار غیرمنتظره شود، جلوگیری میکند.
- دسترسی به Package.json:
- `resolve(process.cwd(), 'package.json')` مسیر مطلق فایل `package.json` را میسازد. `process.cwd()` دایرکتوری کاری فعلی را برمیگرداند و تضمین میکند که مسیر صحیح بدون توجه به محل اجرای دستور استفاده شود.
- `JSON.parse(readFileSync(packageJsonPath, 'utf-8'))` فایل `package.json` را میخواند و تجزیه میکند. `readFileSync` فایل را به صورت همزمان (synchronously) میخواند و `'utf-8'` انکدینگ را برای مدیریت صحیح کاراکترهای یونیکد مشخص میکند. خواندن همزمان در اینجا قابل قبول است زیرا یک بار در ابتدای transform اتفاق میافتد.
- تولید بنر:
- ``const banner = `/**\n * Project: ${packageJson.name}\n * Version: ${packageJson.version}\n */\n`;`` رشته بنر را ایجاد میکند. از template literals (بکتیک) برای جاسازی آسان نام و نسخه پروژه از فایل `package.json` استفاده میکند. توالیهای `\n` خطوط جدیدی برای قالببندی صحیح بنر اضافه میکنند. کاراکتر * با \* escape شده است.
- تغییر کد: `return banner + code;` بنر را به ابتدای کد جاوا اسکریپت اصلی اضافه میکند. این نتیجه نهایی است که توسط تابع transform بازگردانده میشود.
یکپارچهسازی پلاگین
پلاگین را در فایل `vite.config.js` خود وارد کرده و آن را به آرایه `plugins` اضافه کنید:
// vite.config.js
import bannerPlugin from './banner-plugin';
export default {
plugins: [
bannerPlugin(),
],
};
اجرای ساخت
اکنون، دستور `npm run build` (یا دستور ساخت پروژه خود) را اجرا کنید. پس از اتمام ساخت، فایلهای جاوا اسکریپت تولید شده در دایرکتوری `dist` را بررسی کنید. بنر را در بالای هر فایل خواهید دید.
تکنیکهای پیشرفته پلاگین
فراتر از تغییرات ساده کد، پلاگینهای Vite میتوانند از تکنیکهای پیشرفتهتری برای افزایش قابلیتهای خود استفاده کنند.
ماژولهای مجازی
ماژولهای مجازی به پلاگینها اجازه میدهند ماژولهایی ایجاد کنند که به عنوان فایلهای واقعی روی دیسک وجود ندارند. این برای تولید محتوای پویا یا ارائه دادههای پیکربندی به برنامه مفید است.
// virtual-module-plugin.js
export default function virtualModulePlugin(options) {
const virtualModuleId = 'virtual:my-module';
const resolvedVirtualModuleId = '\0' + virtualModuleId; // پیشوند \0 برای جلوگیری از پردازش توسط Rollup
return {
name: 'virtual-module-plugin',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}
},
load(id) {
if (id === resolvedVirtualModuleId) {
return `export default ${JSON.stringify(options)};`;
}
},
};
}
در این مثال:
- `virtualModuleId` یک رشته است که شناسه ماژول مجازی را نشان میدهد.
- `resolvedVirtualModuleId` با `\0` پیشوندگذاری شده است تا Rollup آن را به عنوان یک فایل واقعی پردازش نکند. این یک قرارداد مورد استفاده در پلاگینهای Rollup است.
- `resolveId` فرآیند تحلیل ماژول را رهگیری میکند و اگر شناسه درخواستی با `virtualModuleId` مطابقت داشته باشد، شناسه ماژول مجازی حلشده را برمیگرداند.
- `load` بارگذاری ماژول را رهگیری میکند و اگر شناسه درخواستی با `resolvedVirtualModuleId` مطابقت داشته باشد، کد ماژول را برمیگرداند. در این حالت، یک ماژول جاوا اسکریپت تولید میکند که `options` را به عنوان یک خروجی پیشفرض (default export) صادر میکند.
استفاده از ماژول مجازی
// vite.config.js
import virtualModulePlugin from './virtual-module-plugin';
export default {
plugins: [
virtualModulePlugin({ message: 'Hello from virtual module!' }),
],
};
// main.js
import message from 'virtual:my-module';
console.log(message.message); // خروجی: Hello from virtual module!
تغییر Index HTML
هوک `transformIndexHtml` به شما امکان میدهد فایل `index.html` را تغییر دهید، مانند تزریق اسکریپتها، استایلها یا متا تگها. این برای افزودن ردیابی تحلیلی، پیکربندی متادیتای رسانههای اجتماعی یا سفارشیسازی ساختار HTML مفید است.
// inject-script-plugin.js
export default function injectScriptPlugin() {
return {
name: 'inject-script-plugin',
transformIndexHtml(html) {
return html.replace(
'