Headless WordPress: wzloty i upadki tworzenia niezależnego WordPressa
Opublikowany: 2022-03-10WordPress przeszedł długą drogę od samego początku jako proste narzędzie do pisania blogów. Długie 15 lat później stał się numerem jeden wśród CMS-ów wybieranych zarówno przez programistów, jak i nie-deweloperów. WordPress obsługuje obecnie około 30% z 10 milionów najpopularniejszych witryn w sieci.
Odkąd REST API zostało dołączone do rdzenia WordPressa, programiści mogą eksperymentować i używać go w sposób niezależny, tj. pisać część front-endową za pomocą frameworków lub bibliotek JavaScript. W Infinum używaliśmy (i nadal używamy) WordPressa w „klasyczny” sposób: PHP zarówno dla frontendu, jak i backendu. Po pewnym czasie chcieliśmy spróbować oddzielonego podejścia. W tym artykule podzielę się tym, co chcieliśmy osiągnąć i na co natknęliśmy się, próbując realizować nasze cele.
Istnieje kilka rodzajów projektów, które mogą skorzystać na takim podejściu. Na przykład proste witryny prezentacyjne lub witryny wykorzystujące WordPress jako zaplecze są głównymi kandydatami do podejścia oddzielonego od produkcji.
W ostatnich latach branża na szczęście zaczęła zwracać większą uwagę na wydajność. Jednak będąc łatwym w użyciu, wszechstronnym i wszechstronnym oprogramowaniem, WordPress oferuje mnóstwo opcji, które niekoniecznie są wykorzystywane w każdym projekcie. W rezultacie może ucierpieć wydajność witryny.
Zalecana literatura : Jak korzystać z map ciepła do śledzenia kliknięć w witrynie WordPress
Jeśli długie czasy odpowiedzi na stronie nie pozwalają Ci zasnąć w nocy, to jest dla Ciebie. Omówię podstawy tworzenia niezależnego WordPressa i kilka wyciągniętych wniosków, w tym:
- Znaczenie „oddzielonego WordPressa”
- Praca z domyślnym WordPress REST API
- Poprawa wydajności dzięki oddzielonemu podejściu JSON
- Obawy dotyczące bezpieczeństwa
Czym dokładnie jest WordPress oddzielony?
Jeśli chodzi o sposób programowania WordPress, jedno jest pewne: nie jest on zgodny z wzorcem projektowym Model-View - Controller (MVC), znanym wielu programistom. Ze względu na swoją historię i to, że jest swego rodzaju rozwidleniem starej platformy blogowej o nazwie „b2” (więcej szczegółów tutaj), jest w dużej mierze napisana w sposób proceduralny (przy użyciu kodu opartego na funkcjach). Programiści WordPress Core wykorzystali system hooków, który pozwalał innym programistom modyfikować lub rozszerzać niektóre funkcjonalności.
Jest to system typu „wszystko w jednym”, wyposażony w działający interfejs administratora; zarządza połączeniem z bazą danych i ma wiele przydatnych interfejsów API, które obsługują uwierzytelnianie użytkowników, routing i inne.
Ale dzięki interfejsowi API REST możesz oddzielić backend WordPress jako rodzaj modelu i kontrolera połączone razem, które obsługują manipulację danymi i interakcję z bazą danych, oraz używać kontrolera interfejsu API REST do interakcji z oddzielną warstwą widoku przy użyciu różnych punktów końcowych interfejsu API. Oprócz separacji MVC możemy (ze względów bezpieczeństwa lub poprawy szybkości) umieścić aplikację JS na osobnym serwerze jak na schemacie poniżej:

Zalety stosowania podejścia oddzielonego od produkcji
Jedną z rzeczy, dla których możesz chcieć zastosować to podejście, jest zapewnienie rozdzielenia obaw. Frontend i backend współpracują za pośrednictwem punktów końcowych; każdy z nich może znajdować się na osobnym serwerze, który można zoptymalizować specjalnie dla każdego zadania, tj. osobno uruchamiać aplikację PHP i uruchamiać aplikację Node.js.
Oddzielając swój frontend od zaplecza, łatwiej jest go przeprojektować w przyszłości bez zmiany CMS. Ponadto programiści front-end muszą jedynie dbać o to, co zrobić z danymi, które dostarcza im backend. Dzięki temu mogą być kreatywni i korzystać z nowoczesnych bibliotek, takich jak ReactJS, Vue lub Angular, aby dostarczać bardzo dynamiczne aplikacje internetowe. Na przykład łatwiej jest zbudować progresywną aplikację internetową, korzystając z wyżej wymienionych bibliotek.
Kolejną zaletą jest bezpieczeństwo serwisu. Eksploatacja witryny za pośrednictwem backendu staje się trudniejsza, ponieważ jest w dużej mierze ukryta przed opinią publiczną.
Zalecana literatura : Bezpieczeństwo WordPress jako proces
Wady stosowania podejścia oddzielonego od produkcji
Po pierwsze, posiadanie niezależnego WordPressa oznacza utrzymywanie dwóch oddzielnych instancji:
- WordPress dla zaplecza;
- Oddzielna aplikacja front-end, zawierająca aktualne aktualizacje zabezpieczeń.
Po drugie, niektóre biblioteki front-end mają bardziej stromą krzywą uczenia się. Nauka nowego języka albo zajmie dużo czasu (jeśli jesteś przyzwyczajony do tworzenia szablonów tylko w HTML i CSS), albo będzie wymagać włączenia do projektu dodatkowych ekspertów JavaScript.
Po trzecie, oddzielając frontend, tracisz moc edytora WYSIWYG, a przycisk „Podgląd na żywo” w WordPressie również nie działa.
Praca z WordPress REST API
Zanim zagłębimy się w kod, jeszcze kilka rzeczy o WordPress REST API. Pełna moc interfejsu API REST w WordPressie pojawiła się w wersji 4.7 6 grudnia 2016 r.
Interfejs API REST WordPress umożliwia zdalną interakcję z instalacją WordPress poprzez wysyłanie i odbieranie obiektów JSON.
Konfiguracja projektu
Ponieważ jest dołączony do najnowszej instalacji WordPress, będziemy pracować nad motywem Twenty Seventeen. Pracuję nad Varying Vagrant Vagrants i stworzyłem stronę testową z adresem URL https://dev.wordpress.test/
. Ten adres URL będzie używany w całym artykule. Będziemy również importować posty z repozytorium Wordpress.org Theme Review Teams, dzięki czemu będziemy mieć trochę danych testowych do pracy. Ale najpierw zapoznamy się z pracą z domyślnymi punktami końcowymi, a następnie utworzymy własny niestandardowy punkt końcowy.
Uzyskaj dostęp do domyślnego punktu końcowego REST
Jak już wspomniano, WordPress ma kilka wbudowanych punktów końcowych, które możesz sprawdzić, przechodząc do trasy /wp-json/
:
https://dev.wordpress.test/wp-json/
Umieszczając ten adres URL bezpośrednio w przeglądarce lub dodając go w aplikacji listonosz, otrzymasz odpowiedź JSON z interfejsu API REST WordPressa, która wygląda mniej więcej tak:
{ "name": "Test dev site", "description": "Just another WordPress site", "url": "https://dev.wordpress.test", "home": "https://dev.wordpress.test", "gmt_offset": "0", "timezone_string": "", "namespaces": [ "oembed/1.0", "wp/v2" ], "authentication": [], "routes": { "/": { "namespace": "", "methods": [ "GET" ], "endpoints": [ { "methods": [ "GET" ], "args": { "context": { "required": false, "default": "view" } } } ], "_links": { "self": "https://dev.wordpress.test/wp-json/" } }, "/oembed/1.0": { "namespace": "oembed/1.0", "methods": [ "GET" ], "endpoints": [ { "methods": [ "GET" ], "args": { "namespace": { "required": false, "default": "oembed/1.0" }, "context": { "required": false, "default": "view" } } } ], "_links": { "self": "https://dev.wordpress.test/wp-json/oembed/1.0" } }, ... "wp/v2": { ...
Aby więc pobrać wszystkie posty z naszej witryny za pomocą REST, musielibyśmy przejść do https://dev.wordpress.test/wp-json/wp/v2/posts
. Zauważ, że wp/v2/
oznacza zarezerwowane podstawowe punkty końcowe, takie jak posty, strony, media, taksonomie, kategorie i tak dalej.
Jak więc dodać niestandardowy punkt końcowy?
Utwórz niestandardowy punkt końcowy REST
Załóżmy, że chcemy dodać nowy punkt końcowy lub dodatkowe pole do istniejącego punktu końcowego. Możemy to zrobić na kilka sposobów. Po pierwsze, można to zrobić automatycznie podczas tworzenia niestandardowego typu posta. Na przykład chcemy stworzyć punkt końcowy dokumentacji. Stwórzmy małą wtyczkę testową. Utwórz folder test-documentation w folderze wp-content/plugins i dodaj plik document.php , który wygląda tak:
<?php /** * Test plugin * * @since 1.0.0 * @package test_plugin * * @wordpress-plugin * Plugin Name: Test Documentation Plugin * Plugin URI: * Description: The test plugin that adds rest functionality * Version: 1.0.0 * Author: Infinum <[email protected]> * Author URI: https://infinum.co/ * License: GPL-2.0+ * License URI: https://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: test-plugin */ namespace Test_Plugin; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } /** * Class that holds all the necessary functionality for the * documentation custom post type * * @since 1.0.0 */ class Documentation { /** * The custom post type slug * * @var string * * @since 1.0.0 */ const PLUGIN_NAME = 'documentation-plugin'; /** * The custom post type slug * * @var string * * @since 1.0.0 */ const POST_TYPE_SLUG = 'documentation'; /** * The custom taxonomy type slug * * @var string * * @since 1.0.0 */ const TAXONOMY_SLUG = 'documentation-category'; /** * Register custom post type * * @since 1.0.0 */ public function register_post_type() { $args = array( 'label' => esc_html( 'Documentation', 'test-plugin' ), 'public' => true, 'menu_position' => 47, 'menu_icon' => 'dashicons-book', 'supports' => array( 'title', 'editor', 'revisions', 'thumbnail' ), 'has_archive' => false, 'show_in_rest' => true, 'publicly_queryable' => false, ); register_post_type( self::POST_TYPE_SLUG, $args ); } /** * Register custom tag taxonomy * * @since 1.0.0 */ public function register_taxonomy() { $args = array( 'hierarchical' => false, 'label' => esc_html( 'Documentation tags', 'test-plugin' ), 'show_ui' => true, 'show_admin_column' => true, 'update_count_callback' => '_update_post_term_count', 'show_in_rest' => true, 'query_var' => true, ); register_taxonomy( self::TAXONOMY_SLUG, [ self::POST_TYPE_SLUG ], $args ); } } $documentation = new Documentation(); add_action( 'init', [ $documentation, 'register_post_type' ] ); add_action( 'init', [ $documentation, 'register_taxonomy' ] );
Rejestrując nowy typ posta i taksonomię oraz ustawiając argument show_in_rest
na true
, WordPress automatycznie utworzył trasę REST w przestrzeni nazw /wp/v2/
. Masz teraz dostępne punkty końcowe https://dev.wordpress.test/wp-json/wp/v2/documentation
i https://dev.wordpress.test/wp-json/wp/v2/documentation-category
. Jeśli dodamy post w naszym nowo utworzonym niestandardowym poście z dokumentacją, przechodząc na https://dev.wordpress.test/?post_type=documentation
, otrzymamy odpowiedź, która wygląda tak:
[ { "id": 4, "date": "2018-06-11T19:48:51", "date_gmt": "2018-06-11T19:48:51", "guid": { "rendered": "https://dev.wordpress.test/?post_type=documentation&p=4" }, "modified": "2018-06-11T19:48:51", "modified_gmt": "2018-06-11T19:48:51", "slug": "test-documentation", "status": "publish", "type": "documentation", "link": "https://dev.wordpress.test/documentation/test-documentation/", "title": { "rendered": "Test documentation" }, "content": { "rendered": "
To jest zawartość dokumentacji
\n", "chronione": fałszywe }, "featured_media": 0, "szablon": "", "kategoria-dokumentacji": [ 2 ], "_linki": { "samego siebie": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation/4" } ], "kolekcja": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation" } ], "o": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/types/documentation" } ], "Historia wersji": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation/4/revisions" } ], "wp:załącznik": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/media?parent=4" } ], "wp:termin": [ { "taksonomia": "kategoria dokumentacji", "możliwość osadzenia": prawda, "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation-category?post=4" } ], "Curie": [ { "imię": "wp", "href": "https://api.w.org/{rel}", "szablon": prawda } ] } } ]
To świetny punkt wyjścia dla naszej aplikacji jednostronicowej. Innym sposobem dodania niestandardowego punktu końcowego jest podłączenie do haka rest_api_init
i samodzielne utworzenie punktu końcowego. Dodajmy trasę custom-documentation
, która jest nieco inna niż ta, którą zarejestrowaliśmy. Nadal pracując w tej samej wtyczce, możemy dodać:
/** * Create a custom endpoint * * @since 1.0.0 */ public function create_custom_documentation_endpoint() { register_rest_route( self::PLUGIN_NAME . '/v1', '/custom-documentation', array( 'methods' => 'GET', 'callback' => [ $this, 'get_custom_documentation' ], ) ); } /** * Create a callback for the custom documentation endpoint * * @return string JSON that indicates success/failure of the update, * or JSON that indicates an error occurred. * @since 1.0.0 */ public function get_custom_documentation() { /* Some permission checks can be added here. */ // Return only documentation name and tag name. $doc_args = array( 'post_type' => self::POST_TYPE_SLUG, 'post_status' => 'publish', 'perm' => 'readable' ); $query = new \WP_Query( $doc_args ); $response = []; $counter = 0; // The Loop if ( $query->have_posts() ) { while ( $query->have_posts() ) { $query->the_post(); $post_id = get_the_ID(); $post_tags = get_the_terms( $post_id, self::TAXONOMY_SLUG ); $response[ $counter ]['title'] = get_the_title(); foreach ( $post_tags as $tags_key => $tags_value ) { $response[ $counter ]['tags'][] = $tags_value->name; } $counter++; } } else { $response = esc_html__( 'There are no posts.', 'documentation-plugin' ); } /* Restore original Post Data */ wp_reset_postdata(); return rest_ensure_response( $response ); }
I podłącz metodę create_custom_documentation_endpoint()
do haka rest_api_init
, tak jak poniżej:
add_action( 'rest_api_init', [ $documentation, 'create_custom_documentation_endpoint' ] );
Spowoduje to dodanie niestandardowej trasy w https://dev.wordpress.test/wp-json/documentation-plugin/v1/custom-documentation
z wywołaniem zwrotnym zwracającym odpowiedź dla tej trasy.
[{ "title": "Another test documentation", "tags": ["Another tag"] }, { "title": "Test documentation", "tags": ["REST API", "test tag"] }]
Jest wiele innych rzeczy, które możesz zrobić z REST API (więcej szczegółów znajdziesz w podręczniku REST API).
Obejdź długi czas odpowiedzi podczas korzystania z domyślnego interfejsu API REST
Dla każdego, kto próbował zbudować oddzieloną witrynę WordPress, nie jest to nowa rzecz — REST API działa wolno.
Mój zespół i ja po raz pierwszy zetknęliśmy się z dziwnym, opóźnionym w WordPressie API REST na stronie klienta (nie oddzielonej), gdzie użyliśmy niestandardowych punktów końcowych, aby uzyskać listę lokalizacji na mapie Google, wraz z innymi metainformacjami utworzonymi za pomocą Advanced Custom Fields Pro podłącz. Okazało się, że czas pierwszego bajtu (TTFB) — który jest używany jako wskaźnik odpowiedzi serwera WWW lub innego zasobu sieciowego — trwał ponad 3 sekundy.
Po krótkim badaniu zdaliśmy sobie sprawę, że domyślne wywołania interfejsu API REST były w rzeczywistości bardzo powolne, zwłaszcza gdy „obciążyliśmy” witrynę dodatkowymi wtyczkami. Zrobiliśmy więc mały test. Zainstalowaliśmy kilka popularnych wtyczek i uzyskaliśmy kilka interesujących wyników. Aplikacja listonosz podała czas ładowania 1,97 s dla rozmiaru odpowiedzi 41,9 KB. Czas ładowania Chrome wynosił 1,25 s (TTFB wynosił 1,25 s, zawartość została pobrana w 3,96 ms). Wystarczy pobrać prostą listę postów. Bez taksonomii, bez danych użytkownika, bez dodatkowych pól meta.

Dlaczego się to stało?
Okazuje się, że dostęp do REST API na domyślnym WordPressie załaduje cały rdzeń WordPressa do obsługi punktów końcowych, nawet jeśli nie jest używany. Ponadto im więcej wtyczek dodasz, tym gorzej się stanie. Domyślny kontroler REST WP_REST_Controller
to naprawdę duża klasa, która robi o wiele więcej niż to konieczne podczas budowania prostej strony internetowej. Obsługuje rejestrację tras, sprawdzanie uprawnień, tworzenie i usuwanie elementów i tak dalej.
Istnieją dwa typowe obejścia tego problemu:
- Przechwyć ładowanie wtyczek i zapobiegaj załadowaniu ich wszystkich, gdy potrzebujesz obsłużyć prostą odpowiedź REST;
- Załaduj tylko minimum WordPressa i przechowuj dane w sposób przejściowy, z którego następnie pobieramy dane za pomocą niestandardowej strony.
Poprawa wydajności dzięki oddzielonemu podejściu JSON
Kiedy pracujesz z prostymi witrynami prezentacyjnymi, nie potrzebujesz wszystkich funkcji, które oferuje REST API. Oczywiście tutaj kluczowe jest dobre planowanie. Naprawdę nie chcesz budować swojej witryny bez interfejsu API REST, a za lata powiedzieć, że chcesz połączyć się ze swoją witryną, a może stworzyć aplikację mobilną, która musi korzystać z funkcji interfejsu API REST. Czy ty?
Z tego powodu wykorzystaliśmy dwie funkcje WordPressa, które mogą pomóc w udostępnianiu prostych danych JSON:
- Transients API do buforowania,
- Ładowanie minimum niezbędnego WordPressa za pomocą stałej
SHORTINIT
.
Tworzenie prostego punktu końcowego stron oddzielonych
Stwórzmy małą wtyczkę, która zademonstruje efekt, o którym mówimy. Najpierw dodaj plik wp-config-simple.php do wtyczki json-transient
, który wygląda tak:
<?php /** * Create simple wp configuration for the routes * * @since 1.0.0 * @package json-transient */ define( 'SHORTINIT', true ); $parse_uri = explode( 'wp-content', $_SERVER['SCRIPT_FILENAME'] ); require_once filter_var( $parse_uri[0] . 'wp-load.php', FILTER_SANITIZE_STRING );
define( 'SHORTINIT', true );
zapobiegnie załadowaniu większości podstawowych plików WordPressa, jak widać w pliku wp-settings.php .
Nadal możemy potrzebować niektórych funkcji WordPress, więc możemy ręcznie zażądać pliku (takiego jak wp-load.php ). Ponieważ wp-load.php znajduje się w katalogu głównym naszej instalacji WordPressa, pobierzemy go, pobierając ścieżkę naszego pliku za pomocą $_SERVER['SCRIPT_FILENAME']
, a następnie rozbijając ten ciąg za pomocą ciągu wp-content
. Zwróci to tablicę z dwiema wartościami:
- Korzeń naszej instalacji;
- Reszta ścieżki do pliku (która nas nie interesuje).
Pamiętaj, że używamy domyślnej instalacji WordPressa, a nie zmodyfikowanej, jak na przykład w Bedrock boilerplate, który dzieli WordPressa na inną organizację plików.
Na koniec, ze względów bezpieczeństwa potrzebujemy pliku wp-load.php , z odrobiną oczyszczenia.
W naszym pliku init.php dodamy:
* Author URI: https://infinum.co/ * License: GPL-2.0+ * License URI: https://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: json-transient */ namespace Json_Transient; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } class Init { /** * Get the array of allowed types to do operations on. * * @return array * * @since 1.0.0 */ public function get_allowed_post_types() { return array( 'post', 'page' ); } /** * Check if post type is allowed to be save in transient. * * @param string $post_type Get post type. * @return boolean * * @since 1.0.0 */ public function is_post_type_allowed_to_save( $post_type = null ) { if( ! $post_type ) { return false; } $allowed_types = $this->get_allowed_post_types(); if ( in_array( $post_type, $allowed_types, true ) ) { return true; } return false; } /** * Get Page cache name for transient by post slug and type. * * @param string $post_slug Page Slug to save. * @param string $post_type Page Type to save. * @return string * * @since 1.0.0 */ public function get_page_cache_name_by_slug( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } $post_slug = str_replace( '__trashed', '', $post_slug ); return 'jt_data_' . $post_type . '_' . $post_slug; } /** * Get full post data by post slug and type. * * @param string $post_slug Page Slug to do Query by. * @param string $post_type Page Type to do Query by. * @return array * * @since 1.0.0 */ public function get_page_data_by_slug( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } $page_output = ''; $args = array( 'name' => $post_slug, 'post_type' => $post_type, 'posts_per_page' => 1, 'no_found_rows' => true ); $the_query = new \WP_Query( $args ); if ( $the_query->have_posts() ) { while ( $the_query->have_posts() ) { $the_query->the_post(); $page_output = $the_query->post; } wp_reset_postdata(); } return $page_output; } /** * Return Page in JSON format * * @param string $post_slug Page Slug. * @param string $post_type Page Type. * @return json * * @since 1.0.0 */ public function get_json_page( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } return wp_json_encode( $this->get_page_data_by_slug( $post_slug, $post_type ) ); } /** * Update Page to transient for caching on action hooks save_post. * * @param int $post_id Saved Post ID provided by action hook. * * @since 1.0.0 */ public function update_page_transient( $post_id ) { $post_status = get_post_status( $post_id ); $post = get_post( $post_id ); $post_slug = $post->post_name; $post_type = $post->post_type; $cache_name = $this->get_page_cache_name_by_slug( $post_slug, $post_type ); if( ! $cache_name ) { return false; } if( $post_status === 'auto-draft' || $post_status === 'inherit' ) { return false; } else if( $post_status === 'trash' ) { delete_transient( $cache_name ); } else { if( $this->is_post_type_allowed_to_save( $post_type ) ) { $cache = $this->get_json_page( $post_slug, $post_type ); set_transient( $cache_name, $cache, 0 ); } } } } $init = new Init(); add_action( 'save_post', [ $init, 'update_page_transient' ] );
Metody pomocnicze w powyższym kodzie pozwolą nam na buforowanie:
-
get_allowed_post_types()
Ta metoda informuje typy postów, że chcemy włączyć wyświetlanie w naszym niestandardowym „punkcie końcowym”. Możesz to rozszerzyć i wtyczkę, którą stworzyliśmy, aby można było filtrować tę metodę, dzięki czemu możesz po prostu użyć filtra, aby dodać dodatkowe elementy. -
is_post_type_allowed_to_save()
Ta metoda po prostu sprawdza, czy typ wpisu, z którego próbujemy pobrać dane, znajduje się w dozwolonej tablicy określonej przez poprzednią metodę. -
get_page_cache_name_by_slug()
Ta metoda zwróci nazwę transjentu, z którego zostaną pobrane dane. -
get_page_data_by_slug()
Ta metoda jest metodą, która wykonaWP_Query
w poście za pośrednictwem jego typu slug i post oraz zwróci zawartość tablicy post, którą przekonwertujemy za pomocą JSON przy użyciu metodyget_json_page()
. -
update_page_transient()
Zostanie to uruchomione w hakusave_post
i nadpisze transjent w bazie danych danymi JSON naszego posta. Ta ostatnia metoda jest znana jako „metoda buforowania kluczy”.
Wyjaśnijmy bardziej szczegółowo transjenty.
Transients API
Interfejs API Transients służy do przechowywania danych w tabeli opcji Twojej bazy danych WordPress przez określony czas. Jest to trwała pamięć podręczna obiektów, co oznacza, że przechowujesz jakiś obiekt, na przykład wyniki dużych i powolnych zapytań lub pełne strony, które mogą być utrwalane podczas ładowania stron. Jest podobny do zwykłej pamięci podręcznej obiektów WordPress, ale w przeciwieństwie do WP_Cache
, stany nieustalone będą utrwalać dane podczas ładowania strony, gdzie WP_Cache
(przechowując dane w pamięci) będzie przechowywać dane tylko przez czas trwania żądania.
Jest to magazyn klucz-wartość, co oznacza, że możemy łatwo i szybko pobrać żądane dane, podobnie jak robią to in-memory caching systems
takie jak Memcached lub Redis. Różnica polega na tym, że zwykle musisz zainstalować je osobno na serwerze (co może być problemem na serwerach współdzielonych), podczas gdy przejściowe są wbudowane w WordPress.
Jak zauważono na stronie Codex — transjenty są z natury przyspieszane przez wtyczki buforujące. Ponieważ mogą przechowywać transjenty w pamięci zamiast w bazie danych. Ogólna zasada jest taka, że nie należy zakładać, że stan przejściowy jest zawsze obecny w bazie danych — dlatego dobrą praktyką jest sprawdzenie jego istnienia przed jego pobraniem
$transient_name = get_transient( 'transient_name' ); if ( $transient_name === false ) { set_transient( 'transient_name', $transient_data, $transient_expiry ); }
Możesz go używać bez wygaśnięcia (tak jak my) i dlatego zaimplementowaliśmy rodzaj „pomijania pamięci podręcznej” przy zapisie po zapisie. Oprócz wszystkich wspaniałych funkcji, które zapewniają, mogą pomieścić do 4 GB danych, ale nie zalecamy przechowywania niczego tak dużego w jednym polu bazy danych.
Zalecana lektura : Bądź czujny: funkcje PHP i WordPress, które mogą sprawić, że Twoja witryna stanie się niebezpieczna
Ostateczny punkt końcowy: testowanie i weryfikacja
Ostatnim elementem układanki, którego potrzebujemy, jest „punkt końcowy”. Używam tutaj terminu punkt końcowy, chociaż nie jest to punkt końcowy, ponieważ bezpośrednio wywołujemy określony plik, aby pobrać nasze wyniki. Możemy więc stworzyć plik test.php , który wygląda tak:
get_page_cache_name_by_slug( $post_slug, $post_type ) ); // Return error on false. if ( $cache === false ) { wp_send_json( 'Error, the page does not exist or it is not cached correctly. Please try rebuilding cache and try again!' ); } // Decode json for output. wp_send_json( json_decode( $cache ) );
Jeśli przejdziemy do https://dev.wordpress.test/wp-content/plugins/json-transient/test.php
, zobaczymy ten komunikat:
Błąd, brak informacji o stronie lub typ!
Tak więc musimy określić typ posta i informacje o slugu posta. Gdy przejdziemy teraz do https://dev.wordpress.test/wp-content/plugins/json-transient/test.php?slug=hello-world&type=post
, zobaczymy:
Błąd, strona nie istnieje lub nie jest poprawnie buforowana. Spróbuj odbudować pamięć podręczną i spróbuj ponownie!
Zaczekaj! Najpierw musimy ponownie zapisać nasze strony i posty. Więc kiedy zaczynasz, może to być łatwe. Ale jeśli masz już ponad 100 stron lub postów, może to być trudne zadanie. Dlatego wdrożyliśmy sposób na usunięcie transjentów we wtyczce Decoupled JSON Content i odbudowanie ich w partii.
Ale śmiało i ponownie zapisz wpis Hello World , a następnie ponownie otwórz link. Powinieneś teraz mieć coś, co wygląda tak:
{ "ID": 1, "post_author": "1", "post_date": "2018-06-26 18:28:57", "post_date_gmt": "2018-06-26 18:28:57", "post_content": "Welcome to WordPress. This is your first post. Edit or delete it, then start writing!", "post_title": "Hello world!", "post_excerpt": "", "post_status": "publish", "comment_status": "open", "ping_status": "open", "post_password": "", "post_name": "hello-world", "to_ping": "", "pinged": "", "post_modified": "2018-06-30 08:34:52", "post_modified_gmt": "2018-06-30 08:34:52", "post_content_filtered": "", "post_parent": 0, "guid": "http:\/\/dev.wordpress.test\/?p=1", "menu_order": 0, "post_type": "post", "post_mime_type": "", "comment_count": "1", "filter": "raw" }
I to wszystko. Wtyczka, którą stworzyliśmy, ma kilka dodatkowych funkcji, z których możesz skorzystać, ale w skrócie, w ten sposób możesz pobrać dane JSON z WordPressa, co jest znacznie szybsze niż przy użyciu interfejsu API REST.
Przed i po: Poprawiony czas reakcji
Przeprowadziliśmy testy w Chrome, gdzie mogliśmy osobno zobaczyć całkowity czas odpowiedzi i TTFB. Testowaliśmy czasy odpowiedzi dziesięć razy z rzędu: najpierw bez wtyczek, a potem z dodanymi wtyczkami. Przetestowaliśmy również odpowiedź dla listy postów i pojedynczego posta.
Wyniki testu ilustrują poniższe tabele:


Jak widać różnica jest drastyczna.
Obawy dotyczące bezpieczeństwa
Jest kilka zastrzeżeń, którym należy się dobrze przyjrzeć. Przede wszystkim ręcznie ładujemy podstawowe pliki WordPressa, co w świecie WordPressa jest dużym nie-nie. Czemu? Cóż, poza faktem, że ręczne pobieranie plików podstawowych może być trudne (szczególnie jeśli używasz niestandardowych instalacji, takich jak Bedrock), może to stwarzać pewne obawy dotyczące bezpieczeństwa.
Jeśli zdecydujesz się skorzystać z metody opisanej w tym artykule, upewnij się, że wiesz, jak wzmocnić bezpieczeństwo swojego serwera.
Najpierw dodaj nagłówki HTML jak w pliku test.php :
header( 'Access-Control-Allow-Origin: your-front-end-app.url' ); header( 'Content-Type: application/json' );
Pierwszy nagłówek to sposób na ominięcie środka bezpieczeństwa CORS, dzięki czemu tylko aplikacja frontonu może pobrać zawartość podczas przechodzenia do określonego pliku.
Po drugie, wyłącz przechodzenie katalogów swojej aplikacji. Możesz to zrobić, modyfikując ustawienia nginx
lub dodaj Options -Indexes
do pliku .htaccess , jeśli jesteś na serwerze Apache.
Dodanie tokena do odpowiedzi jest również dobrym środkiem, który może zapobiec niepożądanemu dostępowi. Obecnie pracujemy nad modyfikacją naszej wtyczki Decoupled JSON, aby domyślnie uwzględniać te środki bezpieczeństwa.
Sprawdzenie nagłówka Authorization wysłanego przez aplikację frontendową może wyglądać tak:
if ( ! isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { return; } $auth_header = $_SERVER['HTTP_AUTHORIZATION'];
Następnie możesz sprawdzić, czy określony token (tajne hasło współdzielone tylko przez aplikacje front-endowe i back-endowe) jest podany i poprawny.
Wniosek
REST API jest świetne, ponieważ może służyć do tworzenia pełnoprawnych aplikacji — tworzenia, pobierania, aktualizowania i usuwania danych. Minusem korzystania z niego jest jego szybkość.
Oczywiście tworzenie aplikacji różni się od tworzenia klasycznej strony internetowej. Prawdopodobnie nie będziesz potrzebować wszystkich zainstalowanych przez nas wtyczek. Ale jeśli potrzebujesz danych tylko do celów prezentacyjnych, buforowanie danych i udostępnianie ich w niestandardowym pliku wydaje się obecnie idealnym rozwiązaniem, gdy pracujesz z rozdzielonymi witrynami.
Być może myślisz, że tworzenie niestandardowej wtyczki w celu przyspieszenia czasu reakcji witryny to przesada, ale żyjemy w świecie, w którym liczy się każda sekunda. Wszyscy wiedzą, że jeśli witryna działa wolno, użytkownicy ją porzucają. Istnieje wiele badań, które pokazują związek między wydajnością witryny a współczynnikami konwersji. Ale jeśli nadal potrzebujesz przekonywania, Google karze powolne strony internetowe.
Metoda wyjaśniona w tym artykule rozwiązuje problem z szybkością, który napotyka WordPress REST API i zapewnia dodatkowe przyspieszenie podczas pracy nad oddzielonym projektem WordPress. Ponieważ jesteśmy w niekończącym się dążeniu, aby wycisnąć ostatnią milisekundę z każdego żądania i odpowiedzi, planujemy jeszcze bardziej zoptymalizować wtyczkę. W międzyczasie podziel się swoimi pomysłami na przyspieszenie oddzielonego WordPressa!