Zaawansowany przewodnik po optymalizacji wydajności WordPress

Opublikowany: 2022-03-11

Dziś WordPress obsługuje ponad 30% Internetu. Jest łatwy w użyciu, niezwykle popularny i nigdzie się nie pojawi w najbliższym czasie.

Ale WordPress może być powolny. Jak więc to zoptymalizować?

Istnieje wiele artykułów o tym, jak dostroić i zoptymalizować WordPress. W rzeczywistości sam WordPress zapewnia solidny przewodnik po optymalizacji WordPress.

W większości te artykuły i samouczki obejmują dość podstawowe, ale przydatne koncepcje, takie jak używanie wtyczek pamięci podręcznej, integracja z sieciami dostarczania treści (CDN) i minimalizowanie żądań. Chociaż te wskazówki są bardzo skuteczne, a nawet konieczne, ostatecznie nie rozwiązują podstawowego problemu: większość powolnych witryn WordPress jest wynikiem złego lub nieefektywnego kodu.

Zaawansowany przewodnik po optymalizacji wydajności WordPress

WordPress może być powolny, ale nie musi tak być.
Ćwierkać

Dlatego ten artykuł ma na celu głównie dostarczenie programistom i firmom zajmującym się programowaniem WordPress pewnych wskazówek, które mogą im pomóc w rozwiązaniu podstawowych przyczyn wielu problemów z wydajnością WordPress.

WordPress zapewnia wiele funkcji zorientowanych na wydajność, które często są pomijane przez programistów. Kod, który nie wykorzystuje tych funkcji, może spowolnić najprostsze zadania , takie jak pobieranie postów. W tym artykule opisano cztery możliwe rozwiązania, które rozwiązują niektóre z podstawowych problemów związanych z niską wydajnością WordPressa.

Pobieranie postów

WordPress oferuje możliwość pobrania dowolnego wpisu z bazy danych. Można to zrobić na trzy podstawowe sposoby:

  • Korzystanie z funkcji query_posts() : Jest to bardzo bezpośrednie podejście, ale problem polega na tym, że zastępuje ona główne zapytanie, co może prowadzić do niedogodności. Na przykład, może to być problem, gdybyśmy chcieli ustalić, w pewnym momencie po pobraniu postów (takich jak wewnątrz footer.php ), z jakim rodzajem strony mamy do czynienia. W rzeczywistości oficjalna dokumentacja zawiera notatkę odradzającą korzystanie z tej funkcji, ponieważ będziesz musiał wywołać dodatkową funkcję, aby przywrócić oryginalne zapytanie. Co więcej, zastąpienie głównego zapytania wpłynie negatywnie na czas ładowania strony.

  • Korzystanie z funkcji get_posts() : Działa to prawie jak query_posts() , ale nie modyfikuje głównego zapytania. Z drugiej strony, get_posts() domyślnie wykonuje zapytanie z parametrem suppress_filters ustawionym na true . Może to prowadzić do niespójności, zwłaszcza jeśli w naszym kodzie używamy filtrów związanych z zapytaniami, ponieważ ta funkcja może zwracać posty, których nie oczekujesz na stronie.

  • Korzystanie z klasy WP_Query : Moim zdaniem jest to najlepszy sposób na pobieranie postów z bazy danych. Nie zmienia głównego zapytania i jest wykonywane w standardowy sposób, tak jak każde inne zapytanie WordPress.

Ale bez względu na to, jakiej metody użyjemy do interakcji z bazą danych, musimy wziąć pod uwagę inne rzeczy.

Ograniczenie zapytania

Zawsze powinniśmy określić, ile postów ma pobrać nasze zapytanie.

W tym celu używamy parametru posts_per_page .

WordPress pozwala nam wskazać -1 jako możliwą wartość tego parametru, w którym to przypadku system spróbuje pobrać wszystkie posty spełniające określone warunki.

Nie jest to dobra praktyka, nawet jeśli jesteśmy pewni, że w odpowiedzi otrzymamy tylko kilka wyników.

Po pierwsze, rzadko możemy być pewni, że otrzymamy tylko kilka wyników. A nawet jeśli to możliwe, ustawienie braku limitu będzie wymagało od silnika bazy danych przeskanowania całej bazy danych w poszukiwaniu dopasowań.

I odwrotnie, ograniczenie wyników często umożliwia silnikowi bazy danych tylko częściowe skanowanie danych, co przekłada się na krótszy czas przetwarzania i szybszą odpowiedź.

Inną domyślną rzeczą, którą WordPress robi, a która może negatywnie wpłynąć na wydajność, jest to, że próbuje wyświetlać przyklejone posty i obliczać, ile wierszy zostało znalezionych w zapytaniu.

Często jednak tak naprawdę nie potrzebujemy tych informacji. Dodanie tych dwóch parametrów wyłączy te funkcje i przyspieszy nasze zapytanie:

 $query = new WP_Query( array( 'ignore_sticky_posts' => true, 'no_found_rows' => true ) );

Wykluczanie postów z zapytania

Wordpress Wyklucz posty z zapytania

Czasami chcemy wykluczyć niektóre posty z zapytania. WordPress oferuje dość bezpośredni sposób na osiągnięcie tego: za pomocą parametru post__not_in . Na przykład:

 $posts_to_exclude = array( 1, 2, 3 ); $posts_per_page = 10; $query = new WP_Query( array( 'posts_per_page' => $posts_per_page, 'post__not_in' => $posts_to_exclude ) ); for ( $i = 0; $i < count( $query->posts ); $i++ ) { //do stuff with $query->posts[ $i ] }

Ale chociaż jest to dość proste, nie jest optymalne, ponieważ wewnętrznie generuje podzapytanie. Zwłaszcza w dużych instalacjach może to prowadzić do powolnych odpowiedzi. Szybciej jest pozwolić, aby przetwarzanie zostało wykonane przez interpreter PHP z kilkoma prostymi modyfikacjami:

 $posts_to_exclude = array( 1, 2, 3 ); $posts_per_page = 10; $query = new WP_Query( array( 'posts_per_page' => $posts_per_page + count( $posts_to_exclude ) ) ); for ( $i = 0; $i < count( $query->posts ) && $i < $posts_per_page; $i++ ) { if ( ! in_array( $query->posts[ $i ]->ID, $posts_to_exclude ) ) { //do stuff with $query->posts[ $i ] } }

Co ja tam robiłem?

Zasadniczo zdjąłem trochę pracy z silnika bazy danych i zamiast tego pozostawiłem to silnikowi PHP, który robi to samo, ale w pamięci, która jest znacznie szybsza.

W jaki sposób?

Najpierw usunąłem parametr post__not_in z zapytania.

Ponieważ zapytanie może przynieść nam posty, których nie chcemy w rezultacie, zwiększyłem parametr posts_per_page . W ten sposób upewniam się, że nawet gdybym miał w swojej odpowiedzi jakieś niepożądane posty, miałbym tam co najmniej $posts_per_page pożądanych postów.

Następnie, gdy zapętlam posty, przetwarzam tylko te, których nie ma w tablicy $posts_to_exclude .

Unikanie złożonej parametryzacji

Wszystkie te metody zapytań oferują szeroką gamę możliwości pobierania postów: według kategorii, według metakluczy lub wartości, według daty, według autora itp.

I chociaż ta elastyczność jest potężną cechą, należy jej używać ostrożnie, ponieważ ta parametryzacja może przełożyć się na złożone łączenie tabel i kosztowne operacje na bazach danych.

W następnej sekcji przedstawimy elegancki sposób na osiągnięcie podobnej funkcjonalności bez uszczerbku dla wydajności.

Wyciskanie jak najwięcej z opcji WordPress

Interfejs API opcji WordPress zapewnia szereg narzędzi do łatwego ładowania lub zapisywania danych. Przydaje się do obsługi niewielkich fragmentów informacji, dla których inne mechanizmy oferowane przez WordPress (takie jak posty czy taksonomie) są zbyt skomplikowane.

Zoptymalizowane opcje Wordpress

Na przykład, jeśli chcemy przechowywać klucz uwierzytelniający lub kolor tła nagłówka naszej witryny, szukamy opcji.

WordPress nie tylko daje nam funkcje do ich obsługi, ale także umożliwia nam to w najbardziej efektywny sposób.

Niektóre opcje są nawet ładowane bezpośrednio po uruchomieniu systemu, co zapewnia nam szybszy dostęp (przy tworzeniu nowej opcji musimy zastanowić się, czy chcemy ją automatycznie załadować, czy nie).

Rozważmy na przykład witrynę, na której mamy karuzelę wyświetlającą najświeższe wiadomości, określoną na zapleczu. Naszym pierwszym odruchem byłoby użycie do tego metaklucza w następujący sposób:

 // functions.php add_action( 'save_post', function ( $post_id ) { // For simplicity, we do not include all the required validation before saving // the meta key: checking nonces, checking post type and status, checking // it is not a revision or an autosaving, etc. update_post_meta( $post_id, 'is_breaking_news', ! empty ( $_POST['is_breaking_news'] ) ); } ); // front-page.php $query = new WP_Query( array( 'posts_per_page' => 1, 'meta_key' => 'is_breaking_news' ) ); $breaking_news = $query->posts[0] ?: NULL;

Jak widać, takie podejście jest bardzo proste, ale nie optymalne. Wykona zapytanie do bazy danych, próbując znaleźć post z określonym kluczem meta. Moglibyśmy skorzystać z opcji, aby osiągnąć podobny wynik:

 // functions.php add_action( 'save_post', function ( $post_id ) { // Same comment for post validation if ( ! empty ( $_POST['is_breaking_news'] ) ) update_option( 'breaking_news_id', $post_id ); } ); // front-page.php if ( $breaking_news_id = get_option( 'breaking_news_id' ) ) $breaking_news = get_post( $breaking_news_id ); else $breaking_news = NULL;

Funkcjonalność różni się nieznacznie w zależności od przykładu.

W pierwszym kawałku kodu zawsze będziemy otrzymywać najświeższe wiadomości, jeśli chodzi o datę publikacji posta.

W drugim, za każdym razem, gdy nowy wpis zostanie ustawiony jako najświeższe wiadomości, nadpisze poprzednie wiadomości z ostatniej chwili.

Ale ponieważ prawdopodobnie chcemy mieć jedną najświeższą wiadomość na raz, nie powinno to stanowić problemu.

I na koniec zmieniliśmy ciężkie zapytanie do bazy danych (używając WP_Query z metakluczami) na proste i bezpośrednie zapytanie (wywołanie get_post() ), które jest lepszym i bardziej wydajnym podejściem.

Moglibyśmy również dokonać małej zmiany i użyć transjentów zamiast opcji.

Transjenty działają podobnie, ale pozwalają nam określić czas wygaśnięcia.

Na przykład w przypadku najświeższych wiadomości pasuje jak ulał, ponieważ nie chcemy, aby stary post był najświeższymi wiadomościami, a jeśli pozostawimy zadanie zmiany lub wyeliminowania tej ostatniej wiadomości administratorowi, może on zapomnieć o tym to. Tak więc, za pomocą dwóch prostych zmian, dodajemy datę ważności:

 // functions.php add_action( 'save_post', function ( $post_id ) { // Same comment for post validation // Let's say we want that breaking news for one hour // (3600 = # of seconds in an hour). if ( ! empty ( $_POST['is_breaking_news'] ) ) set_transient( 'breaking_news_id', $post_id, 3600 ); } ); // front-page.php if ( $breaking_news_id = get_transient( 'breaking_news_id' ) ) $breaking_news = get_post( $breaking_news_id ); else $breaking_news = NULL;

Włącz trwałe buforowanie

WordPress natywnie posiada mechanizm buforowania obiektów.

Na przykład opcje są buforowane za pomocą tego mechanizmu.

Ale domyślnie buforowanie nie jest trwałe, co oznacza, że ​​działa tylko przez czas trwania pojedynczego żądania. Wszystkie dane są buforowane w pamięci, aby uzyskać szybszy dostęp, ale są one dostępne tylko podczas tego żądania.

Ilustracja trwałego buforowania

Obsługa trwałego buforowania wymaga zainstalowania wtyczki trwałej pamięci podręcznej.

Niektóre pełnostronicowe wtyczki pamięci podręcznej są dostarczane z dołączoną stałą wtyczką pamięci podręcznej (na przykład W3 Total Cache), ale inne nie i musimy zainstalować ją osobno.

Będzie to zależało od architektury naszej platformy, czy użyjemy plików, Memcached czy jakiegoś innego mechanizmu do przechowywania danych w pamięci podręcznej, ale powinniśmy skorzystać z tej niesamowitej funkcji.

Ktoś mógłby zapytać: „Jeśli to taka świetna funkcja, dlaczego WordPress nie włącza jej domyślnie”?

Głównym powodem jest to, że w zależności od architektury naszej platformy, niektóre techniki pamięci podręcznej będą działać, a inne nie.

Jeśli na przykład hostujemy naszą witrynę na naszym serwerze rozproszonym, powinniśmy użyć zewnętrznego systemu pamięci podręcznej (takiego jak serwer Memcached), ale jeśli nasza witryna znajduje się na jednym serwerze, możemy zaoszczędzić trochę pieniędzy, po prostu korzystając z systemu plików do pamięci podręcznej.

Jedną rzeczą, którą musimy wziąć pod uwagę, jest wygaśnięcie pamięci podręcznej. Jest to najczęstsza pułapka pracy z trwałym buforowaniem.

Jeśli nie rozwiążemy tego problemu prawidłowo, nasi użytkownicy będą skarżyć się, że nie zobaczą wprowadzonych przez siebie zmian lub że ich zastosowanie trwało zbyt długo.

Czasami będziemy musieli szukać kompromisów między wydajnością a dynamiką, ale nawet przy tych przeszkodach uporczywe buforowanie jest czymś, z czego powinna skorzystać praktycznie każda instalacja WordPressa.

AJAXing najszybszy sposób

Jeśli potrzebujemy komunikować się przez AJAX z naszą stroną internetową, WordPress oferuje pewną abstrakcję w momencie przetwarzania żądania po stronie serwera.

Mimo że te techniki mogą być używane podczas programowania narzędzi zaplecza lub przesyłania formularzy z interfejsu użytkownika, należy ich unikać, jeśli nie jest to bezwzględnie konieczne.

Powodem tego jest to, że aby skorzystać z tych mechanizmów, jesteśmy zobowiązani do wysłania żądania postu do jakiegoś pliku znajdującego się w folderze wp-admin . Większość (jeśli nie wszystkie) pełnostronicowych wtyczek do buforowania WordPress nie zawiera żadnych żądań publikacji w pamięci podręcznej ani wywołań do plików administratora.

Na przykład, jeśli dynamicznie ładujemy więcej postów, gdy użytkownik przewija naszą stronę główną, lepiej byłoby bezpośrednio wywołać inną stronę front-endową, która zyska korzyści z buforowania.

Następnie moglibyśmy przeanalizować wyniki za pomocą JavaScript w przeglądarce.

Tak, wysyłamy więcej danych niż potrzebujemy, ale wygrywamy pod względem szybkości przetwarzania i czasu odpowiedzi.

Zniszcz przekonanie, że WordPress jest po prostu wolny

To tylko kilka rad, które programiści powinni wziąć pod uwagę podczas kodowania dla WordPressa.

Czasami zapominamy, że nasza wtyczka lub motyw może wymagać współpracy z innymi wtyczkami lub że nasza witryna może być obsługiwana przez firmę hostingową, która obsługuje setki lub tysiące innych witryn ze wspólną bazą danych.

Skupiamy się tylko na tym, jak wtyczka powinna działać, a nie na tym, jak radzi sobie z tą funkcjonalnością lub jak to zrobić w efektywny sposób.

Z powyższego jasno wynika, że ​​głównymi przyczynami słabej wydajności WordPressa są zły i nieefektywny kod. Jednak WordPress zapewnia wszystkie niezbędne funkcje za pośrednictwem różnych interfejsów API, które mogą nam pomóc w tworzeniu znacznie wydajniejszych wtyczek i motywów bez uszczerbku dla szybkości całej platformy.