静态的 CSS 和 JS 文件虽然高效,但在某些场景下需要根据用户设置、页面内容、站点选项等条件动态生成。比如,用户通过主题自定义器修改了主色,所有页面的颜色需要同步变化;或者根据当前文章的分类,加载不同的自定义样式。在 WordPress 中实现动态 CSS 和 JS 生成,比想象中要简单。

生成动态 CSS 最直接的方法是在 wp_head 钩子中直接输出 <style> 标签。这种方式适合输出少量动态样式:

add_action('wp_head', 'output_dynamic_css');
function output_dynamic_css() {
    $primary_color = get_theme_mod('primary_color', '#0073aa');
    ?>
    <style>
        .site-header { background-color: <?php echo esc_attr($primary_color); ?>; }
        .btn-primary { background-color: <?php echo esc_attr($primary_color); ?>; }
    </style>
    <?php
}

如果动态样式较多,单独输出在头部会显得杂乱,更好的做法是把动态 CSS 作为一个单独的 CSS 文件,通过 PHP 文件生成,然后用 wp_enqueue_style 加载。

创建一个 dynamic-style.php 文件在主题根目录中:

<?php
header('Content-Type: text/css');
require_once(dirname(__FILE__) . '/../../../wp-load.php');

$primary_color = get_theme_mod('primary_color', '#0073aa');
$secondary_color = get_theme_mod('secondary_color', '#f0f0f0');
?>
.site-header { background-color: <?php echo $primary_color; ?>; }
.btn-primary { background-color: <?php echo $primary_color; ?>; }
.sidebar { background-color: <?php echo $secondary_color; ?>; }

然后在 functions.php 中注册这个动态 CSS 文件:

add_action('wp_enqueue_scripts', 'enqueue_dynamic_css');
function enqueue_dynamic_css() {
    wp_enqueue_style('dynamic-style', get_stylesheet_directory_uri() . '/dynamic-style.php', array(), filemtime(__DIR__ . '/dynamic-style.php'));
}

这样浏览器缓存的是动态生成的文件内容,而且文件名不变时浏览器会缓存,适合生产环境。

对于更复杂的场景,你可以把动态 CSS 作为 wp_add_inline_style 附加到某个已加载的样式表之后,这样可以利用已有样式表的缓存:

add_action('wp_enqueue_scripts', 'add_inline_dynamic_css');
function add_inline_dynamic_css() {
    $primary_color = get_theme_mod('primary_color', '#0073aa');
    $inline_css = '.btn-primary { background-color: ' . $primary_color . '; }';
    wp_add_inline_style('main-style', $inline_css);
}

动态 JS 的生成方式类似。你可以在 wp_head 或 wp_footer 中输出 <script>,或者生成一个动态的 JS 文件。动态 JS 常用于传递 PHP 变量给前端 JavaScript:

add_action('wp_enqueue_scripts', 'enqueue_dynamic_js_data');
function enqueue_dynamic_js_data() {
    $user_id = get_current_user_id();
    $ajax_url = admin_url('admin-ajax.php');
    $settings = array(
        'ajaxurl' => $ajax_url,
        'user_id' => $user_id,
        'is_logged_in' => is_user_logged_in()
    );
    wp_localize_script('main-script', 'MySettings', $settings);
}

wp_localize_script 会把 PHP 数组转换为 JavaScript 对象,前端 JS 中可以直接使用 MySettings.ajaxurlMySettings.user_id 等。

对于需要动态加载特定 CSS 或 JS 的场景,你可以根据页面条件在 wp_enqueue_scripts 中判断:

add_action('wp_enqueue_scripts', 'conditionally_load_assets');
function conditionally_load_assets() {
    if (is_page('contact')) {
        wp_enqueue_script('google-maps', 'https://maps.googleapis.com/maps/api/js?key=你的密钥', array(), null, true);
        wp_enqueue_style('contact-style', get_stylesheet_directory_uri() . '/css/contact.css');
    }
    
    if (is_single() && has_category('video')) {
        wp_enqueue_style('video-style', get_stylesheet_directory_uri() . '/css/video.css');
    }
}

动态生成的 CSS 和 JS 如果经常变化,缓存会成为一个问题。除了使用文件修改时间作为版本号,你还可以用 wp_add_inline_style 的方式把动态部分单独输出,而静态部分仍然用缓存文件。

性能方面,动态生成的 CSS/JS 如果体积较大,建议开启服务器的 Gzip 压缩,并在 PHP 文件中设置合适的缓存头。对于不经常变动的动态样式,可以在生成时写入静态文件,只在设置更新时重新生成。这是 WordPress 主题自定义器中常见的技术,也是 Advanced Custom Fields 等插件的高级功能。

对于内容较多的网站,把动态 CSS 和 JS 的控制权交给用户,既满足了定制化需求,又不牺牲加载速度,是一种很好的平衡。