Ha valaha is hozzáadtál „csak egy kis gombot” egy oldalhoz WordPressAztán, amikor 200 KB JavaScriptdel, egy keretrendszertől való függőséggel és egy soha kiürülő gyorsítótárral végzed, szembesülsz az igazi problémával: a kliensoldali interaktivitás gyorsan a bonyolultság rovására megy.

A WordPress már több verzióban is kínál egy „magbarátabb” megközelítést: az Interactivity API-t. Az ötlet papíron egyszerű: deklaráld a kliensoldali viselkedéseket közvetlenül a kódodban (pl. data-wp-*), egy WordPress által kezelt reszponzív áruházzal, React/Vue/Svelte nélkül a témádban.

A probléma / A szükséglet

Konkrét igény: egy front-end komponens létrehozása (pl. „tetszik”, „számláló”, „szűrő”, „harmonika”, „vágólapra másolás”, „chargement progresszív”) interaktív keretrendszer beágyazása nélkül, miközben kompatibilis marad a WordPress ökoszisztémával (gyorsítótár, témák, blokkstémák, oldalkészítők, biztonság).

A végén megtudod:

  • Készíts egy kis bővítményt, ami egy blokkot (vagy egy bárhol használható HTML renderelést) tesz elérhetővé az Interactivity API segítségével.
  • Kliensoldali állapot (tároló) és műveletek (kezelők) kezelése külső függőségek nélkül.
  • Végpont hozzáadása AJAX/REST biztonságos az állapot megőrzése érdekében (pl. like counter).
  • Valós esetek hibakeresése: rossz sorba helyezés, agresszív gyorsítótárazás, rosszul megválasztott hookok, builder ütközések.

Gyors összefoglaló

  • Egy Interactivity API-n alapuló „Like Button” plugint (számláló + váltógomb) kódolunk, amely kompatibilis a WordPress 6.9.4+ és a PHP 8.1+ verziókkal.
  • A jelölés a következőt használja: data-wp-interactive, data-wp-on--click, data-wp-text, data-wp-class--*.
  • A JS tároló kezeli a helyi állapotot (optimistic UI), majd REST API-n keresztül szinkronizál.
  • PHP oldalon: REST végpont /wp-json/bpcab/v1/like, REST nonce, jogosultságok, fertőtlenítés/elérési kód törlése.
  • Rövid kódváltozatot + Divi 5 integrációkat biztosítunk / Elementor / Avada.

Mikor kell használni ezt a megoldást

  • Könnyű interaktivitást szeretnél (kapcsoló, számláló, UI állapot) React csomag nélkül.
  • Egy különféle webhelyekhez (készítők, gyorsítótár, CDN) szánt témát vagy bővítményt szállítasz, és csökkenteni szeretnéd a JS-ütközések kockázatát.
  • Szükséged van egy SSR renderelésre (PHP által generált HTML), ami JS nélkül is használható marad, majd JS-sel „javul” (progresszív fejlesztés).
  • A WordPress mintákon belül kell maradni (sorba helyezés, REST, nonces), és el kell kerülni a sarokban lévő „mini-SPA”-kat.

Mikor NEM szabad ezt a megoldást használni

  • Egy gazdag, egyoldalas alkalmazást (SPA) építesz útvonaltervezéssel, összetett űrlapokkal és hatalmas globális jelentéskészítéssel. Egy keretrendszer (vagy legalább egy robusztusabb architektúra) megfelelőbb lenne.
  • Nagyon régi böngészőket kell támogatnod (az Interactivity API a modern szabványokat követi; ellenőrizd a korlátozásokat).
  • Az interaktivitást már megfelelően biztosítja egy bővítmény (pl. WooCommerce blokkok, dedikált funkciók). A kerék újrafeltalálása költséges fenntartással jár.
  • Valós idejű követelmény van (WebSocket, jelenlét, együttműködés). Az Interactivity API nem helyettesíti a valós idejű réteget.

Előfeltételek / kezdés előtt

  • WordPress 6.9.4 (2026. április) minimum, PHP 8.1+.
  • Tesztkörnyezet (helyi vagy tesztkörnyezet). Kerüld az ilyen típusú kódok közvetlen tesztelését éles környezetben: láttam már, hogy egy rosszul védett REST végpont percek alatt lerobbant.
  • Hozzáférés a permalinkekhez (beállításokhoz) a REST API ellenőrzéséhez és szükség esetén újragenerálásához.
  • Hasznos eszközök:
    • Lekérdezésfigyelő a hookok/REST/teljesítmények vizsgálatához.
    • DevTools böngésző (hálózat + konzol).

Hivatalos források, amiket érdemes kéznél tartani:

Az alapvető monitorozáshoz: ha szokatlan viselkedést tapasztal (különösen a gyorsítótárral vagy a szerkesztővel való interakció során), ellenőrizze a ticketeket a következő helyen: core.trac.wordpress.org és a PR-ek github.com/WordPress/gutenberg.

A naiv megközelítés (és miért kerülendő)

A verzió, amit még mindig gyakran látok: egy gomb egy onclick soron belüli, egy fetch hogy admin-ajax.php, egyetlen kattintás sem, és egy szkript töltődött be mindenhová az oldalon.

<?php
// Exemple à NE PAS copier : inline JS, pas de nonce, pas de contrôle de permission.
echo '<button onclick="likePost(' . get_the_ID() . ')">Like</button>';
?>
<script>
function likePost(postId){
  fetch('/wp-admin/admin-ajax.php?action=like&post_id=' + postId)
    .then(r => r.json())
    .then(console.log);
}
</script>

Mi történik a színfalak mögött:

  • Biztonság: Végpont nonce nélkül = triviális CSRF. És ha nem validálod post_id, megnyitod az ajtót az önkényes írásoknak.
  • Teljesítmény: mindenhová befecskendezett szkript, lehetetlen finoman szabályozni, és nehéz megfelelően gyorsítótározni.
  • Karbantartás: azon a napon, amikor a komponenst hozzáférhetővé (ARIA), tesztelhetővé vagy építőkompatibilissé kell tenni, a nulláról kell kezdeni.
  • DX: A soron belüli kezelők kezelhetetlenné válnak, amint 2 komponensváltozat van.

A helyes megközelítés – lépésről lépésre bemutató

Egy minimalista, letisztult plugint fogunk készíteni egy működő "Like" komponenssel:

  • JS nélkül: a gomb megjelenik (és választhat egy tartalék megoldást).
  • JS-sel: azonnali váltás + frissített számláló, majd REST szinkronizáció.

1. lépés – Hozd létre a bővítményt

Mappa létrehozása wp-content/plugins/bpcab-interactivity-like/ val vel:

  • bpcab-interactivity-like.php
  • assets/like.js

2. lépés – Az „interaktív” HTML-megjelenítés definiálása

Az Interactivity API attribútumokra támaszkodik data-wp-* a jelölésben. A legfontosabb: data-wp-interactive amely egy tároló névteret „csatol” egy DOM részfához.

Létrehozunk egy gombot és egy számlálót. A gomb:

  • műveletet indít el a következőn keresztül: data-wp-on--click
  • dinamikus szöveget jelenít meg a következőn keresztül: data-wp-text
  • CSS osztály váltása data-wp-class--is-liked

3. lépés – A szkript helyes betöltése (célzott sorba állítás)

A klasszikus buktató: a JS betöltése minden oldalon. Itt csak akkor töltjük be a szkriptet, ha a tartalom tartalmazza a rövidkódunkat (vagy ha a blokkunk megjelenik). Az egyszerűség és a megbízhatóság megőrzése érdekében a következőket tesszük:

  • adjon meg egy rövidkódot [bpcab_like]
  • jelenlétének észlelése révén has_shortcode() a megfelelő pillanatban

Megjegyzés: A beépített funkciókkal rendelkező oldalakon (Elementor/Divi) a tartalom eltérően generálódhat. Az alábbiakban bemutatok néhány variációt.

4. lépés – Biztonságos REST végpont létrehozása

Kerüljük admin-ajax.php Ebben az esetben a REST API tisztább, gyorsítótár-barátabb és jobban felszerelt (HTTP kód, JSON, hitelesítés). Létrehozunk egy útvonalat:

  • POST /wp-json/bpcab/v1/like -val post_id et delta (+1 vagy -1)

Védekező eszközökkel védjük:

  • REST egyszeri (wp_create_nonce( 'wp_rest' ))
  • A paraméterek szigorú validálása
  • Engedélyek: Itt a névtelen látogatók engedélyezettek, de a hatásuk korlátozott (delta ±1, meglévő bejegyzés, nyilvános típus). A valódi „felhasználónkénti” perzisztenciához identitásmodellre lenne szükség (felhasználói azonosító, aláírt süti vagy szigorúbb szervertárolás).

5. lépés – Interaktivitás tárolása (optimista felhasználói felület + visszagörgetés)

A minta, amit gyakran használok:

  • Azonnal frissítjük a felhasználói felületet (optimista vagyok)
  • Elküldjük a REST kérést
  • Ha ez nem sikerül, akkor visszaállítjuk az állapotot, és megfelelően naplózunk.

Teljes kód

1. fájl: PHP bővítmény

teremt wp-content/plugins/bpcab-interactivity-like/bpcab-interactivity-like.php.

<?php
/**
 * Plugin Name: BPCAB Interactivity Like
 * Description: Exemple avancé Interactivity API : bouton Like sans framework, avec synchronisation REST.
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 8.1
 * Author: BPCAB
 */

declare(strict_types=1);

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

final class BPCAB_Interactivity_Like_Plugin {
	private const SCRIPT_HANDLE = 'bpcab-like-interactivity';
	private const REST_NAMESPACE = 'bpcab/v1';
	private const REST_ROUTE = '/like';

	public function hooks(): void {
		add_action( 'init', [ $this, 'register_shortcode' ] );
		add_action( 'wp_enqueue_scripts', [ $this, 'maybe_enqueue_assets' ] );
		add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
	}

	public function register_shortcode(): void {
		add_shortcode( 'bpcab_like', [ $this, 'render_shortcode' ] );
	}

	/**
	 * Rend le composant Like.
	 * Utilisable dans un article, une page, un widget texte, ou via do_shortcode().
	 */
	public function render_shortcode( array $atts = [] ): string {
		$atts = shortcode_atts(
			[
				'post_id' => 0,
				'label_like' => 'J’aime',
				'label_unlike' => 'Je n’aime plus',
			],
			$atts,
			'bpcab_like'
		);

		$post_id = (int) $atts['post_id'];
		if ( $post_id <= 0 ) {
			$post_id = get_the_ID() ? (int) get_the_ID() : 0;
		}

		if ( $post_id <= 0 ) {
			return '';
		}

		$post = get_post( $post_id );
		if ( ! $post || 'publish' !== $post->post_status ) {
			return '';
		}

		// Compteur stocké en post meta. Pour un site à fort trafic, préférez une table dédiée ou un agrégat asynchrone.
		$likes = (int) get_post_meta( $post_id, '_bpcab_likes', true );
		if ( $likes < 0 ) {
			$likes = 0;
		}

		// État initial "liked" : ici, on ne persiste pas par utilisateur.
		// On part sur false. Pour une vraie UX, vous pouvez lire un cookie signé ou une table user/post.
		$initial_liked = false;

		// Données initiales injectées dans le DOM (JSON) pour hydrater le store côté client.
		$initial_state = [
			'postId' => $post_id,
			'likes'  => $likes,
			'liked'  => $initial_liked,
			'labels' => [
				'like'   => (string) $atts['label_like'],
				'unlike' => (string) $atts['label_unlike'],
			],
		];

		// Escaping : on encode en JSON, puis on échappe pour attribut HTML.
		$initial_state_json = wp_json_encode( $initial_state, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
		if ( false === $initial_state_json ) {
			return '';
		}

		// Markup Interactivity API.
		// data-wp-interactive="bpcab/like" : namespace du store côté client.
		// data-wp-context : contexte initial (state) accessible via le store.
		$html  = '<div class="bpcab-like"';
		$html .= ' data-wp-interactive="bpcab/like"';
		$html .= ' data-wp-context="' . esc_attr( $initial_state_json ) . '"';
		$html .= '>';

		$html .= '<button type="button" class="bpcab-like__button"';
		$html .= ' data-wp-on--click="actions.toggle"';
		$html .= ' data-wp-class--is-liked="state.liked"';
		$html .= '>';
		$html .= '<span class="bpcab-like__label" data-wp-text="state.liked ? state.labels.unlike : state.labels.like"></span>';
		$html .= '</button>';

		$html .= '<span class="bpcab-like__count" aria-live="polite">';
		$html .= '<strong data-wp-text="state.likes">' . esc_html( (string) $likes ) . '</strong>';
		$html .= '</span>';

		$html .= '</div>';

		return $html;
	}

	/**
	 * Enqueue conditionnel : on charge le JS seulement si le shortcode est présent.
	 * Attention : sur certaines pages builder, le contenu n'est pas dans post_content.
	 */
	public function maybe_enqueue_assets(): void {
		if ( is_admin() ) {
			return;
		}

		$post = get_post();
		if ( ! $post ) {
			return;
		}

		// Détection simple : shortcode présent dans post_content.
		// Variante builder plus bas.
		if ( ! has_shortcode( (string) $post->post_content, 'bpcab_like' ) ) {
			return;
		}

		$asset_url  = plugin_dir_url( __FILE__ ) . 'assets/like.js';
		$asset_path = plugin_dir_path( __FILE__ ) . 'assets/like.js';
		$ver        = file_exists( $asset_path ) ? (string) filemtime( $asset_path ) : '1.0.0';

		// Dépendances : le runtime Interactivity est fourni par WordPress (paquet @wordpress/interactivity).
		// Le handle exact peut évoluer selon les versions ; en pratique WP expose les scripts nécessaires
		// quand vous utilisez l'Interactivity API dans les blocs.
		//
		// Ici, on reste robuste : pas de dépendance forcée, mais on s'appuie sur le fait que WP charge
		// le runtime interactivity quand le markup data-wp-interactive est présent.
		wp_enqueue_script(
			self::SCRIPT_HANDLE,
			$asset_url,
			[],
			$ver,
			[
				'in_footer' => true,
				'strategy'  => 'defer',
			]
		);

		// Données globales minimales : endpoint REST + nonce.
		// Le nonce 'wp_rest' est le standard pour REST API.
		wp_add_inline_script(
			self::SCRIPT_HANDLE,
			'window.BPCAB_LIKE = ' . wp_json_encode(
				[
					'restUrl' => esc_url_raw( rest_url( self::REST_NAMESPACE . self::REST_ROUTE ) ),
					'nonce'   => wp_create_nonce( 'wp_rest' ),
				],
				JSON_UNESCAPED_SLASHES
			) . ';',
			'before'
		);

		// Un peu de CSS minimal via inline (vous pouvez le sortir dans un fichier).
		$css = '.bpcab-like{display:flex;gap:.6rem;align-items:center}.bpcab-like__button.is-liked{font-weight:700}.bpcab-like__count{opacity:.85}';
		wp_register_style( 'bpcab-like-inline', false, [], $ver );
		wp_enqueue_style( 'bpcab-like-inline' );
		wp_add_inline_style( 'bpcab-like-inline', $css );
	}

	public function register_rest_routes(): void {
		register_rest_route(
			self::REST_NAMESPACE,
			self::REST_ROUTE,
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'rest_like' ],
				'permission_callback' => [ $this, 'rest_permission' ],
				'args'                => [
					'post_id' => [
						'type'              => 'integer',
						'required'          => true,
						'sanitize_callback' => 'absint',
						'validate_callback' => function ( $value ) {
							return is_numeric( $value ) && (int) $value > 0;
						},
					],
					'delta' => [
						'type'              => 'integer',
						'required'          => true,
						'sanitize_callback' => function ( $value ) {
							$value = (int) $value;
							if ( 1 === $value ) {
								return 1;
							}
							if ( -1 === $value ) {
								return -1;
							}
							return 0;
						},
						'validate_callback' => function ( $value ) {
							$value = (int) $value;
							return 1 === $value || -1 === $value;
						},
					],
				],
			]
		);
	}

	/**
	 * Permission : on accepte les visiteurs anonymes, mais on exige un nonce REST valide.
	 * Si vous voulez limiter aux utilisateurs connectés : return is_user_logged_in();
	 */
	public function rest_permission( WP_REST_Request $request ): bool|WP_Error {
		$nonce = $request->get_header( 'X-WP-Nonce' );
		if ( ! $nonce ) {
			return new WP_Error( 'bpcab_like_missing_nonce', 'Nonce manquant.', [ 'status' => 403 ] );
		}

		if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
			return new WP_Error( 'bpcab_like_invalid_nonce', 'Nonce invalide.', [ 'status' => 403 ] );
		}

		return true;
	}

	public function rest_like( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$post_id = (int) $request->get_param( 'post_id' );
		$delta   = (int) $request->get_param( 'delta' );

		$post = get_post( $post_id );
		if ( ! $post || 'publish' !== $post->post_status ) {
			return new WP_Error( 'bpcab_like_invalid_post', 'Article introuvable.', [ 'status' => 404 ] );
		}

		// Optionnel : limiter aux post types publics.
		$post_type_obj = get_post_type_object( (string) $post->post_type );
		if ( ! $post_type_obj || ! $post_type_obj->public ) {
			return new WP_Error( 'bpcab_like_forbidden_type', 'Type de contenu non autorisé.', [ 'status' => 403 ] );
		}

		// Mise à jour robuste.
		// Note : update_post_meta n'est pas atomique. Sur très fort trafic, vous pouvez avoir des collisions.
		// Pour un compteur critique, utilisez une table custom + requête SQL atomique, ou un système de queue.
		$current = (int) get_post_meta( $post_id, '_bpcab_likes', true );
		$new     = $current + $delta;
		if ( $new < 0 ) {
			$new = 0;
		}

		update_post_meta( $post_id, '_bpcab_likes', $new );

		return new WP_REST_Response(
			[
				'postId' => $post_id,
				'likes'  => $new,
			],
			200
		);
	}
}

add_action(
	'plugins_loaded',
	static function (): void {
		( new BPCAB_Interactivity_Like_Plugin() )->hooks();
	}
);

2. fájl: Interaktivitási szkript

teremt wp-content/plugins/bpcab-interactivity-like/assets/like.js.

/* global BPCAB_LIKE */
(function () {
  'use strict';

  /**
   * Interactivity API :
   * On enregistre un store "bpcab/like" qui expose state/actions.
   * Le state initial provient de data-wp-context (injecté côté PHP).
   *
   * Référence : https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/
   */
  const { store, getContext } = window.wp && window.wp.interactivity ? window.wp.interactivity : {};

  if (!store || !getContext) {
    // Cas réel : le runtime interactivity n'est pas chargé (mauvais enqueue, optimisation agressive, plugin cache).
    // On évite de casser la page.
    return;
  }

  async function postLike({ postId, delta }) {
    const res = await fetch(BPCAB_LIKE.restUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': BPCAB_LIKE.nonce
      },
      body: JSON.stringify({ post_id: postId, delta })
    });

    if (!res.ok) {
      const text = await res.text().catch(() => '');
      const err = new Error('REST error ' + res.status + ' ' + res.statusText);
      err.details = text;
      throw err;
    }

    return await res.json();
  }

  store('bpcab/like', {
    state: {
      get postId() {
        return getContext().postId;
      },
      get likes() {
        return getContext().likes;
      },
      get liked() {
        return getContext().liked;
      },
      get labels() {
        return getContext().labels || { like: 'J’aime', unlike: 'Je n’aime plus' };
      }
    },
    actions: {
      /**
       * Toggle optimiste :
       * - on inverse liked
       * - on ajuste likes (+1/-1)
       * - on synchronise REST
       * - rollback en cas d'erreur
       */
      async toggle() {
        const ctx = getContext();

        const previousLiked = !!ctx.liked;
        const previousLikes = Number.isFinite(ctx.likes) ? ctx.likes : 0;

        const nextLiked = !previousLiked;
        const delta = nextLiked ? 1 : -1;

        // Mise à jour optimiste.
        ctx.liked = nextLiked;
        ctx.likes = Math.max(0, previousLikes + delta);

        try {
          const data = await postLike({ postId: ctx.postId, delta });

          // On resynchronise avec la vérité serveur.
          if (data && typeof data.likes === 'number') {
            ctx.likes = data.likes;
          }
        } catch (e) {
          // Rollback.
          ctx.liked = previousLiked;
          ctx.likes = previousLikes;

          // Log discret. En prod, vous pouvez envoyer vers un logger.
          // Ne faites pas d'alert() agressif.
          console.error('[bpcab/like] Échec synchronisation', e);
        }
      }
    }
  });
})();

Kód magyarázata

Általános (egyszerű) logika

A PHP egy „normál” HTML fájlt (egy gomb + egy számláló) jelenít meg, és a következőket adja hozzá:

  • data-wp-interactive="bpcab/like" hogy ezt mondja a WordPressnek: „ez a részfa a bpcab/like tárolót használja”.
  • data-wp-context="...json..." a kezdeti állapot befecskendezéséhez (postId, like-ok, liked, labels).
  • data-wp-on--click="actions.toggle" hogy a kattintást egy JS művelethez kapcsolja.
  • data-wp-text="..." az állapotból számított érték megjelenítéséhez.

A JS egy „bpcab/like” tárolót tárol. Amikor a felhasználó rákattint, a kontextus módosul (reaktívvá válik), majd a REST API meghívódik a számláló tárolására.

Technikai részletek (hookok, biztonság, WP 6.9.4 integráció)

  • add_shortcode() Ez praktikus egy hordozható példánál. Éles környezetben gyakran használunk dinamikus blokkot (render_callback) a szerkesztőn belüli finomabb vezérléshez.
  • wp_enqueue_scripts Elölről vizsgáljuk a helyzetet. Az érzékeny pont a jelenlétérzékelés. Ha a készítője nem teszi bele a rövidkódot post_contentA szkript nem töltődik beAz alábbiakban ismertetem a variációkat.
  • REST API :
    • register_rest_route() kiteszi az utat.
    • permission_callback ellenőrizze a nonce-t X-WP-Nonce (CSRF).
    • Szigorú érvényesítés delta ±1-en belül. Enélkül látni fogod, hogy mások posztolnak delta=999999 amint a végpont nyilvánossá válik.
  • A verseny feltételei Egy posztmeta számláló nem atomi. Nagy forgalom esetén két egyidejű lekérdezés felülírhat egy értéket. Egy tipikus blog esetében ez elfogadható; egy nagy volumenű médiafelület esetében egyéni táblázatot és atomi lekérdezéseket (vagy puffert/sorban állást) használok.

Pourquoi data-wp-context nem pedig wp_localize_script példányonként

wp_localize_script globális (handle-ön keresztül), ami nehézkessé válik, ha 10 komponens van ugyanazon az oldalon. Itt minden példány saját JSON-t hordoz. Ez sokkal jobban skálázható, ha cikkekből álló ciklusod van (pl. egy kategóriaoldal 20 "lájkkal").

Változatok és használati esetek

1. változat — Több gomb egy oldalon (ciklus) komplex globális JS nélkül

Teheted [bpcab_like post_id="123"] ciklusban, vagy PHP-n keresztül generáljuk a HTML-t. Minden komponensnek megvan a saját data-wp-contextA tároló megosztott, de a kontextus példányonként változik: pontosan ez a lényeg.

2. változat – A „tetszésnyilvánítások” tárolása (bejelentkezett) felhasználónként egy egyszerű számláló helyett

A használt minta:

  • Ha a felhasználó be van jelentkezve: tárolja a felhasználó metaadataiban a kedvelt bejegyzések listáját (vagy relációs táblázatát).
  • A REST végpont ellenőrzések is_user_logged_in() és felhasználások get_current_user_id().
  • Az összesített számláló a poszt metaadataiban (vagy táblázatában) marad, de a „tetszett” állapot megbízhatóvá válik.

Figyelmeztetés: a felhasználói metaadatokban található lista növekedhet. Nagyobb webhelyeken egy dedikált relációs tábla (user_id, post_id, created_at) + index használata ajánlott.

3. változat – „Nem állandó” mód (csak felhasználói felület) harmonikákhoz/tabokhoz

Ha kizárólag felhasználói felületre van szükséged (harmonika, tabulátorok), távolítsd el a REST végpontot, és csak ezt tartsd meg:

  • data-wp-on--click logikai érték kontextusban történő megváltoztatása
  • data-wp-class--is-open / data-wp-text tükrözni az államot

Tapasztalataim szerint ez a legjobb belépési pont: nulla háttérrendszer, nulla biztonság, és validálod a sorba/gyorsítótár kompatibilitási folyamatát.

Divi 5 / Elementor / Avada kompatibilitás

A leggyakrabban elhangzó pont: A szkript betöltése sikertelen mert a végső tartalom nem tartalmazza a rövidkódot post_content.

Divi 5

A Divi metacímkékben tudja tárolni az elrendezéseket, és a végső megjelenítés nem mindig észlelhető a következőn keresztül: has_shortcode( $post->post_content )Két robusztus lehetőség:

  • „Nagy” lekérdezés csak azokon az oldalakon, ahol tudod, hogy a modul megjelenik (pl. feltételes sablonon keresztül).
  • Vagy a rendereléskor megjelenített jelölőkód jelenlétének észlelése (puffer) – törékenyebb.

Egy gyakran használt pragmatikus megközelítés: egy kis „sorba helyezés kényszerítése” beállítás hozzáadása egy szűrőn keresztül.

<?php
// À mettre dans votre plugin (ou un mu-plugin) si Divi ne déclenche pas has_shortcode().
add_filter( 'bpcab_like_force_enqueue', function( bool $force ): bool {
	if ( function_exists( 'et_theme_builder_get_template_layouts' ) ) {
		// Indice que Divi est actif. À affiner selon votre contexte.
		return true;
	}
	return $force;
}, 10, 1 );

Aztán alkalmazkodj maybe_enqueue_assets() A szűrő alkalmazásához (ha ezt a változatot szeretnéd, csináld rendesen). Alapértelmezés szerint nem adtam hozzá, hogy a bővítmény minimális legyen.

Elementor

Az Elementorral a tartalom gyakran metacímkékből jelenik meg. Két letisztult megoldás:

  • Hozz létre egy dedikált Elementor widgetet, amely megjeleníti a jelölést (és regisztrál egy szkriptfüggőséget az Elementor API-n keresztül).
  • Vagy lekérdezés az Elementor oldalakon egy feltételen keresztül (plugin észlelés + meta).

Ha az egyszerűbb észlelést választja:

<?php
// Exemple : détecter une page construite avec Elementor.
$is_elementor = (bool) get_post_meta( get_the_ID(), '_elementor_edit_mode', true );

Avada (fúziós építő)

Az Avada saját rövidkódokkal és renderelési folyamattal rendelkezik. A legmegbízhatóbb: használja a rövidkódot. [bpcab_like] közvetlenül egy „Kód” vagy „Rövidkód” blokkba/elembe.

Ha az Avada agresszívan minimalizál/összefűz, ellenőrizze a következőket:

  • a szkript nem kerül áthelyezésre head sans defer
  • Az Interactivity futásidejű környezetet az optimalizálás nem „távolítja el”.

Telepítés utáni ellenőrzések

  • Egy olyan oldalon, amely tartalmazza [bpcab_like]Nyisd meg a DevTools > Hálózat menüpontot:
    • ellenőrizd, hogy assets/like.js teljesen fel van töltve (200-as állapot)
    • ellenőrizd, hogy POST /wp-json/bpcab/v1/like kattintásra 200-at ad vissza
  • Fejlesztői eszközök > Konzol:
    • nincs hiba window.wp.interactivity nem definiált
  • A WordPress oldalán:
    • a meta _bpcab_likes frissíti magát (WP-CLI-n vagy egy meta pluginon keresztül ellenőrizhető).

Gyors diagnosztikai táblázat

tünet Valószínű ok igazolás Megoldás
A gomb megjelenik, de nem reagál. A futásidejű interaktivitás nincs betöltve, vagy a JS nincs betöltve Konzol: window.wp.interactivity Létezik? Hálózat: like.js díj ? Kijavítani A sorba helyezés, a JS optimalizálás letiltása, a builder ellenőrzése
403-as hiba a REST útvonalon Hiányzó/érvénytelen nonce Hálózat: fejléc X-WP-Nonce itt? ellenőrzés wp_add_inline_script, HTML gyorsítótár, fejléceket eltávolító CDN
A számláló „visszaugrik” Visszagörgetés REST hiba után Konzol: napló [bpcab/like] Échec synchronisation Ellenőrizd a REST URL-eket, a permalinkeket, a WAF-ot, a CORS-t és a biztonsági bővítményeket.
A mérő terhelés alatt is inkonzisztens Versenyfeltételek a meta bejegyzésben Szimulálja az egyidejű kattintásokat (k6/ab) és hasonlítsa össze őket Egyéni tábla + atomi frissítés, vagy aszinkron aggregáció

Ha ez nem működik

  1. Ellenőrizd, hová illesztetted be a kódot Ennek a bővítménynek benne kell lennie wp-content/plugins/... és aktiválva. Már láttam, hogy a PHP fájlt beillesztették a témába, majd "az egyik oldalon működik, a másikon nem".
  2. PHP ellenőrzése A 8.1 előtti PHP verziókban előfordulhatnak típushibák. Lásd: wp-content/debug.log si WP_DEBUG aktiválva van.
  3. Ellenőrizd a permalinkeket Ha a REST API 404-es hibát ad vissza, lépjen a Beállítások > Állandó linkek menüpontra, és mentse el (változtatás nélkül). Ez egy gyakori probléma a migrációk során.
  4. JS optimalizálás ideiglenes letiltása (cache/minify): Gyakran találkoztam azzal, hogy a futásidejű interaktivitást egy összefűzés „megszakította”, ami megváltoztatta a betöltési sorrendet.
  5. Tesztelje a REST végpontot curl segítségével :
curl -i -X POST "https://example.com/wp-json/bpcab/v1/like" 
  -H "Content-Type: application/json" 
  -H "X-WP-Nonce: VOTRE_NONCE" 
  --data '{"post_id":123,"delta":1}'

Ha nincs kéznél nonce objektum, legalább teszteld le, hogy létezik-e az útvonal (403-as „Hiányzó nonce objektum” hibát kell visszaadnia, ami jó jel).

Gyakori buktatók és hibák

Hiba Okoz Megoldás
Uncaught TypeError: Cannot destructure property 'store' ... window.wp.interactivity hiányzik (futásidejű nincs betöltve) Ellenőrizd a sorba helyezést, tiltsd le a minify-t, és győződj meg róla, hogy a WP betölti az interaktivitási szkripteket.
A szkript soha nem töltődik be has_shortcode() nem érzékeli (builder, meta segítségével renderelve) Adj hozzá egy feltételszerkesztőt (Elementor/Divi/Avada), vagy helyezd sorba egy dedikált widget/modul segítségével.
403 Nonce invalide Lejárt nonce elemmel gyorsítótárolt HTML, vagy teljes oldalas gyorsítótáron keresztül kiszolgált oldal Zárd ki az oldalt a gyorsítótárból, vagy generáld a nonce elemet egy nyilvános végponton keresztül, vagy korlátozd a bejelentkezett felhasználókra.
SyntaxError: Unexpected token -ban data-wp-context A JSON helytelenül kódolt/escaped karakterekkel ellátott, hibás idézőjelek Mindig menj át wp_json_encode akkor esc_attr
Végzetes hiba másolás-beillesztés után Hiányzó zárójel/pontosvessző, vagy a fájl rossz mappába került Validálás linterrel, WP_DEBUG engedélyezése a teszteléskor, szintaxis javítása
A számláló negatívvá válik A delta nincs validálva, vagy gyors dupla kattintás Érvényesíti delta ±1, 0-ra kell szorítani mind a szerver, mind a kliens oldalon
Ütközés egy biztonsági/WAF bővítménnyel REST POST kérések vagy fejlécek blokkolása Az útvonal fehérlistára helyezése /wp-json/bpcab/v1/likeWAF naplók ellenőrzése
Egy régi, 2023-ban talált részletet használsz. Az API/kezelők megváltoztak, a gyakorlatok elavultak A jelenlegi interaktivitási dokumentáció alapján a WP 6.9.4+ verzióját célozza meg.

Biztonsági, teljesítménybeli és karbantartási tippek

Biztonság

  • REST egyszeri érték szükséges Ha a végpont módosítja az adatokat. Egyszeri hiba nélkül, CSRF.
  • Erősítse meg a beállításokat itt delta szigorúan ±1. Ez egy egyszerű, de hatékony akadály.
  • Gondolj a bántalmazásra Egy nyilvános számláló manipulálható. Ha a metrika üzleti értékkel rendelkezik, add hozzá:
    • sebességkorlátozás (a fordított proxy/WAF szintjén)
    • tárhely felhasználónként (bejelentkezett) vagy aláírt süti szerint
    • botészlelés

Teljesítmény

  • Feltételes lekérdezés: betöltés elkerülése like.js mindenhol.
  • Kerüld a felesleges REST kéréseket: hozzáadhatsz egy pattanásgátlót, ha engedélyezed a többszörös kattintásokat (itt ki-be kapcsoljuk, így stabil).
  • Nagy forgalom: metaadat utáni + nem atomi frissítés. Ha csúcsokat tapasztalsz, válts egyéni táblára és atomi SQL frissítésre (vagy késleltetett aggregációra).

Karbantartás

  • Tartsa a tárolót egy egyértelmű névtérben (bpcab/like), kerülje az általános neveket (app).
  • Kerüld a logikád összekapcsolását egy builderrel. Hozz létre egy integrációs réteget (widget/modul), ha szükséges.
  • Az Interactivity API fejlesztéseinek figyelése a Gutenberg (PR) és a kézikönyv segítségével. A változások először a Gutenberg oldalon érkeznek meg, majd beépülnek a magba.

erőforrás

FAQ

Az Interactivity API helyettesíti a Reactet a Gutenbergben?

Nem. A Gutenberg továbbra is a Reactet használja a szerkesztőhöz. Az Interactivity API elsősorban a blokkok/komponensek front-end interaktivitását célozza, egy könnyebb és deklaratívabb modellel.

Miért az én window.wp.interactivity nincs meghatározva?

A gyakorlatban ez vagy egy sorba helyezési probléma (a szkript túl korán töltődött be/egyáltalán nem töltődött be), egy JavaScript optimalizálás, amely megtöri a sorrendet, vagy egy olyan tartalomkészítő, amely a feltételek aktiválása nélkül generál tartalmat. Ideiglenesen tiltsa le az agresszív minify/defer műveleteket, és ellenőrizze a hálózati beállításokat.

Ez kompatibilis egy teljes oldalas gyorsítótárral?

Igen a megjelenítéshez. A perzisztencia érdekében légy óvatos a nonce karakterrel: ha a HTML oldal egy gyorsítótárból érkezik, és lejárt nonce karaktert tartalmaz, a REST POST kéréseid sikertelenek lesznek (403). A nagy sebességgel gyorsítótárolt webhelyeken jobban szeretem a nonce karaktert egy nyilvános végponton keresztül (csak olvasható) generálni, vagy a funkcionalitást a bejelentkezett felhasználókra korlátozni.

Miért érdemes REST-et használni az admin-ajax helyett?

A REST letisztultabb HTTP szemantikát és szabványos JSON válaszokat biztosít, valamint jobban integrálódik a modern eszközökkel. Az admin-ajax továbbra is érvényes, de gyorsan egy mindent elsöprő kóddá válik, amelyet nehéz biztonságossá tenni és profilálni.

Használhatunk Gutenberg blokkot rövidkód helyett?

Igen, és ez gyakran előnyösebb. A minta lényege ugyanaz marad: SSR renderelés + attribútumok data-wp-* + áruház. Rövid kódot használok, így 5 perc alatt tesztelheted, beleértve a fejlesztőket is.

Hogyan kezelhetek több különböző számlálót (lájkok, könyvjelzők, szavazatok)?

Használjon külön névtereket (bpcab/like, bpcab/bookmark) vagy egyetlen, kontextus által konfigurált tároló. Kerülje a „monolitikus” tárolót, ha az összetevői függetlenek.

Megbízható a post-metában található számláló?

Egy tipikus blog esetében: igen. Nagy forgalom esetén: nem, ütközések lesznek. Váltson egyéni táblázatra és atomi frissítésekre, vagy használjon aszinkron aggregációs stratégiát.

Hogyan tudom ezt a kódot megfelelően tesztelni?

Általában ezt szoktam csinálni:

  • Átállás + WP_DEBUG napló
  • Manuális teszt: gyorskattintások, navigáció, böngésző gyorsítótár
  • REST teszt: curl (nonce nélkül 403 várható, nonce-szal 200)
  • Gyorsítótár kompatibilitási teszt: gyorsítótár engedélyezése, nonce lejárat ellenőrzése

Miért nem helyezik be a nunciust? data-wp-context ?

Megteheted, de minden példányban egy érzékeny értéket fogsz megkettőzni. Én egy minimális globális objektumot részesítek előnyben, majd komponensenként külön kontextust a felhasználói felület állapotához. Ha nagyon hosszú oldalaid vannak, ez csökkenti a HTML méretét.

Klasszikus (nem FSE) témában működik?

Igen. Az Interactivity API nem korlátozódik blokkstémákra. A lényeg az elemek helyes betöltése és a jelölőnyelv attribútumokkal való megjelenítése. data-wp-*.

Mit tegyek, ha egy kódrészlet bővítmény hibásan működik?

Kerüld el ennek a bővítménynek a beillesztését egy kódrészletbe. Hozz létre egy megfelelő bővítményt (mint ez). A kódrészletek kényelmesek, de túl sok oldalt láttam már összeomlani egy másik PHP verzión aktivált kódrészlet, vagy egy hiányzó kapcsos zárójeles másolás-beillesztés miatt.