اگر تا امروز قالب وردپرس نوشته باشی، احتمالاً با این چالش‌ها روبه‌رو شدی:
کند بودن build، سختی مدیریت فایل‌ها، نبود hot reload درست‌وحسابی و از همه بدتر، لود شدن کلی اسکریپت و استایل روی صفحاتی که اصلاً بهشون نیاز ندارن.

حالا تصور کن بتونی:

برای هر صفحه وردپرس (خانه، سبد خرید، پرداخت و…) اسکریپت مخصوص خودش رو داشته باشی

  • فقط همون فایل‌ها روی همون صفحه لود بشن
  • در حالت توسعه، تغییرات رو لحظه‌ای ببینی
  • و در نهایت یک خروجی تمیز و بهینه برای production بگیری

اینجاست که Vite وارد بازی می‌شه.

در این آموزش قراره قدم‌به‌قدم یاد بگیریم چطور از Vite داخل قالب وردپرس استفاده کنیم، طوری که هم توسعه سریع‌تر بشه و هم خروجی نهایی بهینه و حرفه‌ای باشه.

این آموزش برای چه کسیه؟

  1. آشنایی پایه با قالب‌نویسی وردپرس داری
  2. wp_enqueue_script و wp_enqueue_style برات غریبه نیست
  3. SCSS رو در حد معمول استفاده کردی
  4. دنبال یک راه‌حل تمیز و قابل توسعه هستی

Vite چیست و چرا برای وردپرس انتخاب خوبی است؟

Vite یک ابزار مدرن برای توسعه فرانت‌اند است که تمرکزش روی سرعت و سادگیه.
برخلاف ابزارهای قدیمی‌تر مثل Webpack، در حالت توسعه فایل‌ها رو bundle نمی‌کنه، بلکه مستقیم از ES Module مرورگر استفاده می‌کنه.

  • مزیت‌های Vite برای قالب وردپرس:
  • اجرای سریع Dev Server
  • Hot Module Replacement واقعی
  • خروجی تمیز و ماژولار
  • مدیریت ساده چند entry (خیلی مهم برای وردپرس)
  • جایگزین مدرن برای Laravel Mix یا Webpack

سناریوی ما چیست؟

فرض کن داری یک قالب وردپرس می‌سازی که:

  • یک فایل global.js برای کل سایت داره
  • یک فایل home.js فقط برای صفحه اصلی
  • یک فایل cart.js فقط برای صفحه سبد خرید
  • و استایل‌ها هم بر اساس صفحه جدا شدن

هدف اینه که:

اسکریپت و استایل مربوط به هر صفحه، فقط در همان صفحه لود شود.

ساختار پروژه

resources
├─ css
├─ js
│  ├─ global.js
│  ├─ home.js
│  ├─ cart.js
├─ scss
│  ├─ base
│  ├─ bootstrap
│  │  └─ _bootstrap.scss
│  ├─ components
│  ├─ core
│  │  ├─ _base.scss
│  │  ├─ _fonts.scss
│  │  ├─ _mixins.scss
│  │  └─ _variables.scss
│  ├─ layout
│  │  ├─ _header.scss
│  │  └─ _footer.scss
│  ├─ pages
│  │  ├─ home.scss
│  │  └─ cart.scss
│  ├─ _shared.scss
│  └─ global.scss
vite.config.js

چرا این ساختار؟

اسکریپت‌ها و استایل‌ها تفکیک‌شده باشن

هر صفحه فایل مخصوص خودش رو داشته باشه

نگهداری پروژه راحت تر بشه

  • core/
    چیزهایی که همه جای سایت استفاده می‌شن
    (variables، mixins، reset، فونت‌ها)
  • layout/
    ساختار کلی سایت
    (هدر، فوتر، گرید اصلی)
  • components/
    اجزای تکرارشونده
    (دکمه، کارت، فرم)
  • pages/
    استایل‌های مخصوص هر صفحه
    (خانه، سبد خرید، پرداخت)
  • global.scss
    نقطه ورود اصلی CSS سایت

این ساختار باعث می‌شه:

  • CSS قابل نگهداری باشه
  • فایل‌ها رشد کنن بدون اینکه به هم بریزن
  • هر صفحه استایل خودش رو داشته باشه

اتصال SCSS به JS

Vite فقط وقتی CSS رو build می‌کنه که از داخل JS ایمپورت شده باشه.

global.js

import '../scss/global.scss';

console.log('Global scripts loaded');

home.js

import '../scss/pages/home.scss';

console.log('Home page scripts');

cart.js

import '../scss/pages/cart.scss';

console.log('Cart page scripts');

هر entry، CSS مخصوص خودش رو وارد می‌کنه

Vite خودش CSS رو استخراج می‌کنه

وردپرس بعداً از manifest می‌فهمه چی لود کنه

نوشتن SCSS به شکل درست

_bootstrap.scss

$font-family-base: IRANYekanXFaNum, Tahoma, sans-serif;

@use "bootstrap/scss/bootstrap";

global.scss

@use 'core/variables';
@use 'core/mixins';
@use 'core/fonts';
@use 'core/base';

@use 'layout/header';
@use 'layout/footer';
@use 'components/buttons';

home.scss

.home-hero {
  padding: 4rem;
}

cart.scss

.cart-page {
  background: #f5f5f5;
}

چرا @use؟

  • scope امن‌تر
  • جلوگیری از تداخل متغیرها
  • best practice رسمی Sass

کانفیگ Vite برای وردپرس

حالا می‌رسیم به مهم‌ترین بخش: فایل vite.config.js

این فایل در مسیر اصلی قالب قرارمیگیره یعنی:

wp-content/themes/your-theme

تعریف وابستگی‌های اسکریپت‌ها

گاهی بعضی entryها به اسکریپت‌های وردپرس مثل jquery نیاز دارن.
ما این وابستگی‌ها رو این‌جا تعریف می‌کنیم:

const assetDeps = {
    global: {
        js: ['jquery'],
    },
    home: {
        js: ['jquery'],
    },
    cart: {
        js: ['jquery'],
    }
};
  • این یعنی:
    global.js به jquery وابسته است
  • home.js هم همین‌طور
  • cart.js هم همین‌طور

اضافه کردن وابستگی‌ها به manifest وردپرس

برای اینکه وردپرس بفهمه هر فایل به چی وابسته است، یک پلاگین کوچک برای Vite می‌نویسیم:

به زبان ساده:

  • بعد از build
  • فایل manifest رو می‌خونیم
  • برای هر entry وابستگی‌هاش رو اضافه می‌کنیم
function wpManifestDepsPlugin() {
    return {
        name: 'wp-manifest-deps',
        apply: 'build',

        closeBundle() {
            const manifestPath = path.resolve(__dirname, 'dist/.vite/manifest.json');
            if (!fs.existsSync(manifestPath)) return;

            const manifest = JSON.parse(
                fs.readFileSync(manifestPath, 'utf-8')
            );

            Object.values(manifest).forEach(entry => {
                if (!entry.isEntry) return;

                const entryName = entry.name;
                const deps = assetDeps[entryName] || { js: [] };

                entry['deps-js'] = deps.js || [];
            });

            fs.writeFileSync(
                manifestPath,
                JSON.stringify(manifest, null, 2)
            );
        }
    };
}

پیدا کردن خودکار فایل‌های JS

نیازی نیست هر بار entryها رو دستی تعریف کنیم. با استفاده از تابع زیر vite خودش همه فایل‌های داخل resources/js رو پیدا می‌کنه:

function getEntries() {
    const jsFiles = glob.sync('resources/js/*.js');
    const entries = {};

    jsFiles.forEach(file => {
        const name = path.basename(file, '.js');
        entries[name] = path.resolve(__dirname, file);
    });

    return entries;
}

فایل کامل vite.config.js

import { defineConfig } from 'vite';
import path from 'path';
import fs from 'fs';
import { glob } from 'glob';


const assetDeps = {
    global: {
        js: ['jquery'],
    },
    home: {
        js: ['jquery'],
    },
    cart: {
        js: ['jquery'],
    }
};

//وابستگی فایل های js را مشخص میکنه
function wpManifestDepsPlugin() {
    return {
        name: 'wp-manifest-deps',
        apply: 'build',

        closeBundle() {
            const manifestPath = path.resolve(__dirname, 'dist/.vite/manifest.json');
            if (!fs.existsSync(manifestPath)) return;

            const manifest = JSON.parse(
                fs.readFileSync(manifestPath, 'utf-8')
            );

            Object.values(manifest).forEach(entry => {
                if (!entry.isEntry) return;

                const entryName = entry.name;
                const deps = assetDeps[entryName] || { js: [] };

                entry['deps-js'] = deps.js || [];
            });

            fs.writeFileSync(
                manifestPath,
                JSON.stringify(manifest, null, 2)
            );
        }
    };
}

//  پیدا کردن تمام فایل‌های JS در resources/js
function getEntries() {
    const jsFiles = glob.sync('resources/js/*.js');

    const entries = {};

    jsFiles.forEach(file => {
        // تبدیل 'resources/js/home.js' به 'home'
        const name = path.basename(file, '.js');
        entries[name] = path.resolve(__dirname, file);
    });

    return entries;
}

export default defineConfig(({ command }) => ({
    root: __dirname,
    base: command === 'build'
          ? '/wp-content/themes/your-theme/dist/'
          : '/',
    build: {
        outDir: 'dist',
        emptyOutDir: true,
        manifest: '.vite/manifest.json',
        rollupOptions: {
            input: getEntries(),
            output: {
                entryFileNames: 'assets/[name].[hash].js',
                chunkFileNames: 'assets/[name].[hash].js',
                assetFileNames: 'assets/[name].[hash].[ext]'
            }
        }
    },
    server: {
        port: 5173,
        strictPort: true,
        host: 'localhost',//نام دامنه مجازی در سرور لوکال مثلا: your-project.local
        cors: true,
        hmr: {
            host: 'localhost',//نام دامنه مجازی در سرور لوکال مثلا: your-project.local
        }
    },
    plugins: [
        wpManifestDepsPlugin()
    ]
}));

لود کردن فایل‌ها در وردپرس (Dev و Production)

اینجا جاییه که وردپرس و Vite به هم می‌رسن.

تعریف قوانین لود فایل‌ها در وردپرس

این تابع مشخص می‌کنه:

  • کدوم entry
  • در چه صفحه‌ای
  • با چه وابستگی‌هایی لود بشه
function get_vite_entry_rules() {
	return [
		'global' => [
			'condition' => true, // همیشه لود می‌شود
			'deps'      => [ 'jquery' ]
		],

		'home' => [
			'condition' => is_front_page(),
			'deps'      => [ 'jquery' ]
		],

		'cart' => [
			'condition' => function_exists( 'is_cart' ) && is_cart(),
			'deps'      => [ 'jquery' ]
		],

		'product' => [
			'condition' => function_exists( 'is_product' ) && is_product(),
			'deps'      => [ 'jquery' ]
		],

		'shop' => [
			'condition' => function_exists( 'is_shop' ) && is_shop(),
			'deps'      => [ 'jquery' ]
		],

		'checkout' => [
			'condition' => function_exists( 'is_checkout' ) && is_checkout(),
			'deps'      => [ 'jquery' ]
		],

		'admin'     => [
			'condition' => is_admin(),
			'deps'      => []
		],

		// صفحات سفارشی با template
		'contact'   => [
			'condition' => is_page_template( 'templates/contact.php' ),
			'deps'      => [ 'jquery' ]
		],

		// صفحات با ID مشخص
		'about'     => [
			'condition' => is_page( 5 ), // ID صفحه درباره ما
			'deps'      => [ 'jquery' ]
		],

		// پست تایپ سفارشی
		'portfolio' => [
			'condition' => is_singular( 'portfolio' ),
			'deps'      => [ 'jquery' ]
		]
	];
}

enqueue کامل فایل‌ها (Dev + Production)

این تابع:

  • تشخیص می‌ده Dev هستیم یا Build
  • فایل‌ها رو از جای درست لود می‌کنه
  • فقط entryهای مورد نیاز صفحه فعلی رو enqueue می‌کنه
function enqueue_vite_assets() {
	$is_dev = true;// true/false
	$entry_rules = get_vite_entry_rules();

	if ($is_dev) {
		// ===== Dev Mode =====

		// 1 Vite Client
		wp_enqueue_script(
			'vite-client',
			'http://localhost:5173/@vite/client',
			[],
			null,
			false
		);
		add_filter('script_loader_tag', function($tag, $handle) {
			if ($handle === 'vite-client') {
				return str_replace('<script', '<script type="module"', $tag);
			}
			return $tag;
		}, 10, 2);

		// 2 Load فقط entryهای مرتبط با صفحه فعلی
		foreach ($entry_rules as $name => $rule) {
			// چک کردن شرط
			$should_load = is_callable($rule['condition'])
				? call_user_func($rule['condition'])
				: $rule['condition'];

			if (!$should_load) {
				continue;
			}

			// Load JS
			wp_enqueue_script(
				"vite-{$name}",
				"http://localhost:5173/resources/js/{$name}.js",
				array_merge(['vite-client'], $rule['deps'] ?? []),
				null,
				true
			);

			add_filter('script_loader_tag', function($tag, $handle) use ($name) {
				if ($handle === "vite-{$name}") {
					return str_replace('<script', '<script type="module"', $tag);
				}
				return $tag;
			}, 10, 2);
		}

	} else {
		// ===== Build Mode =====

		$manifest_path = get_template_directory() . '/dist/.vite/manifest.json';

		if (!file_exists($manifest_path)) {
			return;
		}

		$manifest = json_decode(file_get_contents($manifest_path), true);

		foreach ($manifest as $key => $item) {
			if (!isset($item['isEntry']) || !$item['isEntry']) {
				continue;
			}

			$name = $item['name'];

			// چک کردن آیا این entry باید لود بشه
			if (!isset($entry_rules[$name])) {
				continue;
			}

			$should_load = is_callable($entry_rules[$name]['condition'])
				? call_user_func($entry_rules[$name]['condition'])
				: $entry_rules[$name]['condition'];

			if (!$should_load) {
				continue;
			}

			$js_deps = array_merge(
				$entry_rules[$name]['deps'] ?? [],
				$item['deps-js'] ?? []
			);

			$css_deps = $item['deps-css'] ?? [];

			// Load JS
			if (isset($item['file'])) {
				wp_enqueue_script(
					$name,
					get_template_directory_uri() . '/dist/' . $item['file'],
					$js_deps,
					null,
					true
				);
				wp_script_add_data($name, 'type', 'module');
			}

			// Load CSS
			if (isset($item['css'])) {
				foreach ($item['css'] as $css_file) {
					wp_enqueue_style(
						$name . '-css',
						get_template_directory_uri() . '/dist/' . $css_file,
						$css_deps,
						null
					);
				}
			}
		}
	}
}

اتصال به وردپرس (اکشن)

add_action('wp_enqueue_scripts', 'enqueue_vite_assets');

اجرای پروژه

با استفاده از CMD در ویندوز یا Terminal به مسیری که قالب شما در آن هست وارد بشین

نصب وابستگی ها:

npm install

حالت توسعه

npm run dev

حالت production

npm run build

بعد از انجام این مراحل:

  • Vite روی قالب وردپرس فعاله
  • SCSS ساختاریافته و قابل توسعه داری
  • هر صفحه فقط فایل‌های خودش رو لود می‌کنه
  • Dev و Production کاملاً جدا و تمیز

اگر این آموزش رو تا اینجا اجرا کردی یا وسطش به مشکلی خوردی، حتماً توی کامنت‌ها بنویس.

سوال‌ها، تجربه‌هات یا حتی باگ‌هایی که دیدی می‌تونه به کامل‌تر شدن این آموزش کمک کنه.