WordPress模板优化

关键点:由于需要缓存页面,因此页面中不能包含个人数据及动态数据。

使用<esi:include>标签优化

1、通过使用shortcode([esi src=”/esi/cart-fragment/”])或者函数(<?php esi_include(‘/esi/cart-fragment/’);?>)的方式,在个人数据/动态数据部分进行替换。

2、通过注册endpoints,使varnish访问该endpoints时,获取动态数据,替换esi标签内容。

<?php
/*
Plugin Name: ESI for Varnish - Simple Fragments
Description: Provide simple ESI fragment endpoints and helpers for WordPress + WooCommerce to be used with Varnish ESI. Adds endpoints: /esi/cart-fragment/, /esi/header-user/, /esi/wc-mini-cart/. Includes template helper esi_include() and shortcode [esi src="/esi/..."].
Version: 0.9
Author: RQQ (generated)
License: GPLv2+
*/

if (! defined('ABSPATH')) {
    exit;
}

/**
 * -----------------------
 * Configuration (adjust)
 * -----------------------
 */
if (! defined('ESI_CACHE_TTL')) define('ESI_CACHE_TTL', 30); // seconds for dynamic fragments (keep small)
if (! defined('ESI_CACHE_PRIVATE')) define('ESI_CACHE_PRIVATE', true); // fragments often per-user -> private or no-cache
if (! defined('ESI_VARNISH_PURGE_URL')) define('ESI_VARNISH_PURGE_URL', ''); // e.g. http://127.0.0.1:80/ (empty = disabled)
if (! defined('ESI_ALLOWED_ORIGINS')) define('ESI_ALLOWED_ORIGINS', ''); // optional for CORS if needed

/**
 * -----------------------
 * Helpers
 * -----------------------
 */
function esi_send_fragment_headers( $is_private = true, $ttl = ESI_CACHE_TTL ) {
    // Surrogate-Control indicates to Varnish that the response may contain ESI or be handled specially
    header_remove('Cache-Control'); // ensure consistent
    if ( $is_private ) {
        // private fragment (per user) -> don't be cached by shared caches for others; but Varnish can still request it and insert into user response
        header('Cache-Control: private, max-age=' . intval($ttl));
    } else {
        header('Cache-Control: public, max-age=' . intval($ttl));
    }
    // Informative header for Varnish ESI usage (some setups use Surrogate-Control)
    header('Surrogate-Control: ESI/1.0');
    if (ESI_ALLOWED_ORIGINS) {
        header('Access-Control-Allow-Origin: ' . ESI_ALLOWED_ORIGINS);
    }
    header('Content-Type: text/html; charset=UTF-8');
}

/**
 * Utility: purge key on Varnish (optional)
 * Tries to call a simple PURGE (or BAN) URL if configured. Very simple implementation.
 */
function esi_varnish_purge( $path ) {
    if ( empty(ESI_VARNISH_PURGE_URL) ) return false;
    $url = rtrim(ESI_VARNISH_PURGE_URL, '/') . $path;
    // do a simple request; suppress failures
    $args = array(
        'method'      => 'PURGE',
        'timeout'     => 2,
        'redirection' => 0,
    );
    // WP HTTP may not allow PURGE method; try with custom header POST fallback
    $response = wp_remote_request( $url, $args );
    return $response;
}

/**
 * -----------------------
 * Register endpoints (rewrite + template redirect)
 * -----------------------
 */

// Add rewrite rules on init
add_action('init', function () {
    add_rewrite_rule('^esi/cart-fragment/?$', 'index.php?esi_fragment=cart-fragment', 'top');
    add_rewrite_rule('^esi/header-user/?$', 'index.php?esi_fragment=header-user', 'top');
    add_rewrite_rule('^esi/wc-mini-cart/?$', 'index.php?esi_fragment=wc-mini-cart', 'top');

    add_rewrite_tag('%esi_fragment%', '([^&]+)');
});

// Handle template redirect for endpoints
add_action('template_redirect', function () {
    $frag = get_query_var('esi_fragment');
    if ( ! $frag ) return;

    // Prevent WP themes from sending their normal HTML wrapper
    // We respond and exit.

    if ($frag === 'cart-fragment') {
        // Very small HTML fragment: cart count + mini html
        // Use WC functions if available
        if ( function_exists('WC') ) {
            $count = WC()->cart ? WC()->cart->get_cart_contents_count() : 0;
        } else {
            $count = 0;
        }
        esi_send_fragment_headers( true, ESI_CACHE_TTL );
        // Example structure — keep minimal so insertion is fast
        echo '<div class="esi-cart-fragment" data-esi="cart-fragment">';
        echo '<a class="esi-mini-cart" href="' . esc_url( wc_get_cart_url() ? wc_get_cart_url() : '/cart/' ) . '">';
        echo '<span class="esi-cart-count">' . intval( $count ) . '</span>';
        echo '</a></div>';
        exit;
    }

    if ($frag === 'header-user') {
        esi_send_fragment_headers( true, ESI_CACHE_TTL );
        if ( is_user_logged_in() ) {
            $current = wp_get_current_user();
            echo '<div class="esi-header-user">Hello, ' . esc_html( $current->display_name ) . ' | <a href="' . esc_url( wp_logout_url() ) . '">Logout</a></div>';
        } else {
            echo '<div class="esi-header-user"><a href="' . esc_url( wp_login_url() ) . '">Login</a></div>';
        }
        exit;
    }

    if ($frag === 'wc-mini-cart') {
        // More advanced mini-cart HTML — keep concise
        esi_send_fragment_headers( true, ESI_CACHE_TTL );
        if ( function_exists('WC') ) {
            $items = WC()->cart->get_cart();
            echo '<div class="esi-wc-mini-cart">';
            if ( empty( $items ) ) {
                echo '<div class="empty">Cart empty</div>';
            } else {
                echo '<ul>';
                foreach ( $items as $cart_item_key => $cart_item ) {
                    $p = $cart_item['data'];
                    $title = $p ? $p->get_name() : '';
                    echo '<li>' . esc_html( $title ) . ' x ' . intval( $cart_item['quantity'] ) . '</li>';
                }
                echo '</ul>';
            }
            echo '</div>';
        } else {
            echo '<div class="esi-wc-mini-cart empty">Cart unavailable</div>';
        }
        exit;
    }


    // // default: 404
    // status_header(404);
    // exit;
});

/**
 * -----------------------
 * Template helper and shortcode
 * -----------------------
 */

/**
 * Output an ESI include tag (prints raw <esi:include ... />).
 * Use this in theme templates where you want a fragment.
 *
 * Example: <?php esi_include('/esi/cart-fragment/'); ?>
 */
function esi_include( $path ) {
    $path = esc_url( $path );
    echo '<esi:include src="' . esc_attr( $path ) . '" />';
    return;
}

/**
 * Shortcode [esi src="/esi/cart-fragment/"]
 */
add_shortcode('esi', function ( $atts ) {
    $a = shortcode_atts( array(
        'src' => '',
    ), $atts, 'esi' );
    $src = trim( $a['src'] );
    if ( empty( $src ) ) return '';
    return esi_include($src); //只需内部的相对url
});

/**
 * -----------------------
 * Auto-insert into Astra header (best-effort, non-destructive)
 * -----------------------
 * If Astra provides hook 'astra_header_after' we add an ESI cart fragment there.
 * This is optional and only runs if the hook exists.
 */
// if ( has_action('astra_header_after') ) {
//     add_action('astra_header_after', function () {
//         // print ESI include for cart fragment — keep minimal HTML wrapper
//         esi_include('/esi/cart-fragment/');
//     }, 5 );
// }

/**
 * -----------------------
 * Cache invalidation hooks
 * -----------------------
 * When post saved / product updated / order updated, remove fragment transients and optionally call Varnish purge.
 */
function esi_purge_fragments_for_post( $post_id ) {
    // delete any fragment caches if you use object cache keys here (example)
    // This plugin uses live generation for fragments, but we still trigger varnish purge if configured
    if ( defined('ESI_VARNISH_PURGE_URL') && ESI_VARNISH_PURGE_URL ) {
        // purge specific fragment URLs
        $paths = array(
            '/esi/cart-fragment/',
            '/esi/header-user/',
            '/esi/wc-mini-cart/',
        );
        foreach ( $paths as $p ) {
            esi_varnish_purge( $p );
        }
    }
}
add_action('save_post', 'esi_purge_fragments_for_post', 10, 1);
add_action('woocommerce_update_product', 'esi_purge_fragments_for_post', 10, 1);
add_action('woocommerce_cart_updated', 'esi_purge_fragments_for_post', 10, 1);
add_action('woocommerce_update_order', 'esi_purge_fragments_for_post', 10, 1);

/**
 * -----------------------
 * Admin: simple settings link
 * -----------------------
 */
add_filter('plugin_action_links_' . plugin_basename(__FILE__), function ( $links ) {
    $links[] = '<a href="' . admin_url( 'options-general.php' ) . '">Settings</a>';
    return $links;
});


/**
 * -----------------------
 * Activation: flush rewrite rules
 * -----------------------
 */
register_activation_hook( __FILE__, function () {
    // flush rewrite rules so our /esi/* rules take effect
    flush_rewrite_rules();
});
register_deactivation_hook( __FILE__, function () {
    flush_rewrite_rules();
});

使用cart_fragments方式处理动态数据

1、页面中个人数据及动态数据留空,并做好css标记,以便ajax进行替换

2、注册fragments

/**
该代码执行后,会向后台发送ajax请求,到 /?wc-ajax=get_refreshed_fragments ,返回数据格式示例:
{
    "fragments": {
        "h2.elementor-heading-title": "aaaaaaaaaaaaaaaaa"
    },
    "cart_hash": "bc522a0236b33854f5bc36d6f538f6a7"
}

DOM树的h2.elementor-heading-title选择器的内容,会被替换为aaaaaaaaaaaaaaaaa
*/
add_filter('woocommerce_add_to_cart_fragments',function($fragments){
     $fragments['h2.elementor-heading-title'] = 'aaaaaaaaaaaaaaaaa';
     return $fragments;
 }, 20);
Scroll to Top