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:
- Interaktivitási API (Blokkszerkesztő kézikönyv)
- REST API kézikönyv
- register_rest_route()
- wp_enqueue_script ()
- PHP szűrő/érvényesítés (php.net)
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.phpassets/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-valpost_idetdelta(+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_scriptsElö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ódotpost_contentA szkript nem töltődik beAz alábbiakban ismertetem a variációkat.- REST API :
register_rest_route()kiteszi az utat.permission_callbackellenőrizze a nonce-tX-WP-Nonce(CSRF).- Szigorú érvényesítés
delta±1-en belül. Enélkül látni fogod, hogy mások posztolnakdelta=999999amint 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ásokget_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--clicklogikai érték kontextusban történő megváltoztatásadata-wp-class--is-open/data-wp-texttü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
headsansdefer - 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.jsteljesen fel van töltve (200-as állapot) - ellenőrizd, hogy
POST /wp-json/bpcab/v1/likekattintásra 200-at ad vissza
- ellenőrizd, hogy
- Fejlesztői eszközök > Konzol:
- nincs hiba
window.wp.interactivitynem definiált
- nincs hiba
- A WordPress oldalán:
- a meta
_bpcab_likesfrissíti magát (WP-CLI-n vagy egy meta pluginon keresztül ellenőrizhető).
- a meta
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
- 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". - PHP ellenőrzése A 8.1 előtti PHP verziókban előfordulhatnak típushibák. Lásd:
wp-content/debug.logsiWP_DEBUGaktiválva van. - 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.
- 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.
- 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
deltaszigorú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.jsmindenhol. - 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
- Interaktivitási API (developer.wordpress.org)
- REST API kézikönyv
- register_rest_route() (hivatkozás)
- wp_create_nonce() (hivatkozás)
- wp_verify_nonce() (hivatkozás)
- WordPress Core Trac (hibakövetés)
- Gutenberg adattár (PR Interactivity)
- filter_var() (php.net)
- WordPress.org támogatási fórumok (valós esetek)
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.