ヘッドレスWordPress:分離されたWordPressを作成することの浮き沈み
公開: 2022-03-10WordPressは、シンプルなブログ作成ツールとしての当初から長い道のりを歩んできました。 長い15年後、それは開発者と非開発者の両方にとってCMSの第一の選択肢となりました。 WordPressは現在、Web上の上位1,000万サイトの約30%に電力を供給しています。
REST APIがWordPressコアにバンドルされて以来、開発者はそれを分離した方法で実験して使用できます。つまり、JavaScriptフレームワークまたはライブラリを使用してフロントエンド部分を作成できます。 Infinumでは、WordPressを「クラシック」な方法で使用していました(現在も使用しています)。フロントエンドとバックエンドにPHPを使用しています。 しばらくして、分離されたアプローチを試してみたかったのです。 この記事では、私たちが達成したかったことと、私たちの目標を実行しようとしたときに遭遇したことの概要を共有します。
このアプローチから利益を得ることができるプロジェクトのいくつかのタイプがあります。 たとえば、単純なプレゼンテーションサイトや、WordPressをバックエンドとして使用するサイトは、分離アプローチの主な候補です。
近年、業界はありがたいことにパフォーマンスに注目し始めました。 ただし、WordPressは、使いやすく、包括的で用途の広いソフトウェアであるため、すべてのプロジェクトで必ずしも使用されるとは限らない多数のオプションが付属しています。 その結果、Webサイトのパフォーマンスが低下する可能性があります。
おすすめの読み物:ヒートマップを使用してWordPressWebサイトのクリックを追跡する方法
長いウェブサイトの応答時間が夜にあなたを維持するならば、これはあなたのためのハウツーです。 分離されたWordPressの作成の基本と、以下を含むいくつかの教訓について説明します。
- 「分離されたWordPress」の意味
- デフォルトのWordPressRESTAPIの操作
- 分離されたJSONアプローチによるパフォーマンスの向上
- セキュリティ上の懸念
では、分離されたWordPressとは正確には何ですか?
WordPressのプログラミング方法に関して言えば、確かなことが1つあります。それは、多くの開発者が精通しているM odel- V iew- C ontroller(MVC)デザインパターンに準拠していないことです。 その歴史と「b2」と呼ばれる古いブログプラットフォームのフォークのようなものであるため(詳細はこちら)、主に手続き型の方法で記述されています(関数ベースのコードを使用)。 WordPressのコア開発者は、他の開発者が特定の機能を変更または拡張できるようにするフックのシステムを使用しました。
これは、機能する管理インターフェースを備えたオールインワンシステムです。 データベース接続を管理し、ユーザー認証やルーティングなどを処理する便利なAPIを多数公開しています。
しかし、REST APIのおかげで、WordPressバックエンドを、データ操作とデータベースの相互作用を処理する一種のモデルとコントローラーとして分離し、REST APIコントローラーを使用して、さまざまなAPIエンドポイントを使用して別のビューレイヤーと相互作用することができます。 MVCの分離に加えて、(セキュリティ上の理由または速度の向上のために)JSアプリを以下のスキーマのように別のサーバーに配置できます。
分離アプローチを使用する利点
このアプローチを使用する理由の1つは、関心の分離を確実にすることです。 フロントエンドとバックエンドはエンドポイントを介して相互作用しています。 それぞれが個別のサーバー上にあり、それぞれのタスクごとに特別に最適化できます。つまり、PHPアプリとNode.jsアプリを個別に実行します。
フロントエンドをバックエンドから分離することで、CMSを変更せずに、将来的にフロントエンドを再設計するのが簡単になります。 また、フロントエンドの開発者は、バックエンドが提供するデータをどう処理するかだけを気にする必要があります。 これにより、クリエイティブになり、ReactJS、Vue、Angularなどの最新のライブラリを使用して、非常に動的なWebアプリを提供できます。 たとえば、前述のライブラリを使用すると、プログレッシブWebアプリを簡単に作成できます。
もう1つの利点は、Webサイトのセキュリティに反映されています。 バックエンドを介してWebサイトを利用することは、大部分が一般に公開されていないため、より困難になります。
推奨読書:プロセスとしてのWordPressセキュリティ
分離アプローチを使用することの欠点
まず、WordPressを分離するということは、2つの別々のインスタンスを維持することを意味します。
- バックエンド用のWordPress。
- タイムリーなセキュリティアップデートを含む、独立したフロントエンドアプリ。
第二に、フロントエンドライブラリのいくつかはより急な学習曲線を持っています。 新しい言語を学ぶのに多くの時間がかかるか(テンプレート作成のためにHTMLとCSSに慣れている場合)、またはプロジェクトに追加のJavaScriptエキスパートを連れてくる必要があります。
第3に、フロントエンドを分離すると、WYSIWYGエディターの機能が失われ、WordPressの[ライブプレビュー]ボタンも機能しなくなります。
WordPress RESTAPIの操作
コードを深く掘り下げる前に、WordPress RESTAPIについてさらにいくつか説明します。 WordPressのRESTAPIの全機能は、2016年12月6日にバージョン4.7で提供されました。
WordPress REST APIでできることは、JSONオブジェクトを送受信することでWordPressインストールをリモートで操作することです。
プロジェクトの設定
最新のWordPressインストールがバンドルされているため、TwentySeventeenテーマに取り組んでいます。 私はVaryingVagrant Vagrantsに取り組んでおり、 https://dev.wordpress.test/
を使用してテストサイトを設定しました。 このURLは記事全体で使用されます。 また、wordpress.orgテーマレビューチームリポジトリから投稿をインポートして、使用するテストデータを取得します。 ただし、最初にデフォルトのエンドポイントの操作に慣れてから、独自のカスタムエンドポイントを作成します。
デフォルトのRESTエンドポイントにアクセスする
すでに述べたように、WordPressには、 /wp-json/
ルートに移動して調べることができるいくつかの組み込みエンドポイントが付属しています。
https://dev.wordpress.test/wp-json/
このURLをブラウザに直接配置するか、postmanアプリに追加すると、WordPress RESTAPIから次のようなJSON応答が得られます。
{ "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": { ...
したがって、RESTを使用してサイト内のすべての投稿を取得するには、 https://dev.wordpress.test/wp-json/wp/v2/posts
にアクセスする必要があります。 wp/v2/
は、投稿、ページ、メディア、分類法、カテゴリなどの予約済みのコアエンドポイントをマークしていることに注意してください。
では、カスタムエンドポイントを追加するにはどうすればよいですか?
カスタムRESTエンドポイントを作成する
新しいエンドポイントまたは追加のフィールドを既存のエンドポイントに追加するとします。 それを行うにはいくつかの方法があります。 まず、カスタム投稿タイプを作成するときに自動的に実行できます。 たとえば、ドキュメントエンドポイントを作成したいとします。 小さなテストプラグインを作成しましょう。 wp-content / pluginsフォルダーにtest-documentationフォルダーを作成し、次のようなdocumentation.phpファイルを追加します。
<?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' ] );
新しい投稿タイプと分類法を登録し、 show_in_rest
引数をtrue
に設定することで、WordPressは/wp/v2/
名前空間にRESTルートを自動的に作成しました。 これで、 https://dev.wordpress.test/wp-json/wp/v2/documentation
およびhttps://dev.wordpress.test/wp-json/wp/v2/documentation-category
エンドポイントが利用可能になりました。 新しく作成したドキュメントのカスタム投稿にhttps://dev.wordpress.test/?post_type=documentation
に投稿を追加すると、次のような応答が返されます。
[ { "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": "
これはいくつかのドキュメントコンテンツです
\ n "、 「保護された」:false }、 "featured_media":0、 "テンプレート": ""、 "ドキュメント-カテゴリ":[ 2 ]、 "_links":{ 「自己」:[ {{ "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation/4" } ]、 「コレクション」:[ {{ "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation" } ]、 "だいたい": [ {{ "href": "https://dev.wordpress.test/wp-json/wp/v2/types/documentation" } ]、 "バージョン履歴":[ {{ "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation/4/revisions" } ]、 "wp:attachment":[ {{ "href": "https://dev.wordpress.test/wp-json/wp/v2/media?parent = 4" } ]、 "wp:term":[ {{ "taxonomy": "documentation-category"、 「埋め込み可能」:true、 "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation-category?post = 4" } ]、 「キュリー」:[ {{ "名前": "wp"、 "href": "https://api.w.org/ {rel}"、 「テンプレート化された」:true } ] } } ]
これは、シングルページアプリケーションの優れた出発点です。 カスタムエンドポイントを追加するもう1つの方法は、 rest_api_init
フックにフックして、自分でエンドポイントを作成することです。 登録したものとは少し異なるcustom-documentation
ルートを追加しましょう。 同じプラグインで引き続き機能し、次を追加できます。
/** * 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 ); }
そして、次のように、 create_custom_documentation_endpoint()
メソッドをrest_api_init
フックにフックします。
add_action( 'rest_api_init', [ $documentation, 'create_custom_documentation_endpoint' ] );
これにより、 https://dev.wordpress.test/wp-json/documentation-plugin/v1/custom-documentation
にカスタムルートが追加され、コールバックがそのルートの応答を返します。
[{ "title": "Another test documentation", "tags": ["Another tag"] }, { "title": "Test documentation", "tags": ["REST API", "test tag"] }]
REST APIでできることは他にもたくさんあります(詳細については、REST APIハンドブックを参照してください)。
デフォルトのRESTAPIを使用する場合の長い応答時間の回避
分離されたWordPressサイトを構築しようとした人にとって、これは新しいことではありません— RESTAPIは遅いです。
私のチームと私は、クライアントサイト(分離されていない)で奇妙なWordPressの遅れているREST APIに最初に遭遇しました。そこでは、カスタムエンドポイントを使用して、 Advanced Custom FieldsProを使用して作成された他のメタ情報とともにGoogleマップ上の場所のリストを取得しました。プラグイン。 Webサーバーまたは他のネットワークリソースの応答性の指標として使用される最初のバイト(TTFB)に3秒以上かかることが判明しました。
少し調べてみると、特に追加のプラグインでサイトに「負担をかけた」場合、デフォルトのRESTAPI呼び出しが実際には非常に遅いことがわかりました。 そこで、小さなテストを行いました。 人気のあるプラグインをいくつかインストールしたところ、興味深い結果が得られました。 postmanアプリは、41.9KBの応答サイズに対して1.97秒のロード時間を与えました。 Chromeの読み込み時間は1.25秒でした(TTFBは1.25秒、コンテンツは3.96ミリ秒でダウンロードされました)。 投稿の簡単なリストを取得するだけです。 分類法、ユーザーデータ、追加のメタフィールドはありません。
なぜこれが起こったのですか?
デフォルトのWordPressでRESTAPIにアクセスすると、使用されていない場合でも、エンドポイントにサービスを提供するためにWordPressコア全体が読み込まれることがわかりました。 また、追加するプラグインが多いほど、事態は悪化します。 デフォルトのRESTコントローラーWP_REST_Controller
は非常に大きなクラスであり、単純なWebページを構築するときに必要以上のことを行います。 ルートの登録、権限チェック、アイテムの作成と削除などを処理します。
この問題には、2つの一般的な回避策があります。
- プラグインのロードをインターセプトし、単純なREST応答を提供する必要があるときにプラグインをすべてロードしないようにします。
- 最小限のWordPressのみをロードし、データをトランジェントに保存します。トランジェントから、カスタムページを使用してデータをフェッチします。
分離されたJSONアプローチによるパフォーマンスの向上
単純なプレゼンテーションサイトで作業している場合、RESTAPIが提供するすべての機能は必要ありません。 もちろん、これは適切な計画が重要な場所です。 REST APIを使用せずにサイトを構築したくない場合は、数年後にサイトに接続するか、RESTAPI機能を使用する必要のあるモバイルアプリを作成することをお勧めします。 あなたは?
そのため、単純なJSONデータを提供する際に役立つ2つのWordPress機能を利用しました。
- キャッシング用のトランジェントAPI、
-
SHORTINIT
定数を使用して必要最小限のWordPressをロードします。
単純な分離ページエンドポイントの作成
私たちが話している効果を示す小さなプラグインを作成しましょう。 まず、次のようなwp-config-simple.phpファイルをjson-transient
プラグインに追加します。
<?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 );
wp-settings.phpファイルに見られるように、WordPressコアファイルの大部分がロードされないようにします。
まだWordPress機能の一部が必要な場合があるため、ファイル( wp-load.phpなど)を手動で要求できます。 wp-load.phpはWordPressインストールのルートにあるため、 $_SERVER['SCRIPT_FILENAME']
を使用してファイルのパスを取得し、その文字列をwp-content
文字列で展開してフェッチします。 これにより、次の2つの値を持つ配列が返されます。
- インストールのルート。
- 残りのファイルパス(これは私たちには関係ありません)。
WordPressのデフォルトのインストールを使用しており、たとえば、WordPressを別のファイル編成に分割するBedrockボイラープレートのように変更されたインストールを使用していないことに注意してください。
最後に、セキュリティのために、少しサニタイズしたwp-load.phpファイルが必要です。
init.phpファイルに、以下を追加します。
* 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' ] );
上記のコードのヘルパーメソッドを使用すると、キャッシュを実行できます。
-
get_allowed_post_types()
このメソッドは、投稿タイプに、カスタムの「エンドポイント」での表示を有効にすることを通知します。 これを拡張することができ、プラグインは実際にこのメソッドをフィルター可能にしたので、フィルターを使用してアイテムを追加することができます。 -
is_post_type_allowed_to_save()
このメソッドは、データをフェッチしようとしている投稿タイプが、前のメソッドで指定された許可された配列にあるかどうかを確認するだけです。 -
get_page_cache_name_by_slug()
このメソッドは、データがフェッチされるトランジェントの名前を返します。 -
get_page_data_by_slug()
このメソッドは、スラッグと投稿タイプを介して投稿WP_Query
を実行し、get_json_page()
メソッドを使用してJSONで変換する投稿配列の内容を返すメソッドです。 -
update_page_transient()
これはsave_post
フックで実行され、データベース内のトランジェントを投稿のJSONデータで上書きします。 この最後の方法は、「キーキャッシュ方法」として知られています。
過渡現象についてさらに詳しく説明しましょう。
トランジェントAPI
Transients APIは、WordPressデータベースのオプションテーブルに特定の期間データを保存するために使用されます。 これは永続化されたオブジェクトキャッシュです。つまり、大きくて遅いクエリの結果や、ページの読み込みを超えて永続化できるページ全体など、オブジェクトを保存しているということです。 これは通常のWordPressオブジェクトキャッシュに似ていますが、 WP_Cache
とは異なり、トランジェントはページの読み込み全体でデータを保持しますWP_Cache
(メモリにデータを保存する)はリクエストの期間中のみデータを保持します。
これはKey-Valueストアです。つまり、MemcachedやRedisなどin-memory caching systems
と同様に、目的のデータを簡単かつ迅速にフェッチできます。 違いは、通常はサーバーに個別にインストールする必要があることです(これは共有サーバーで問題になる可能性があります)が、トランジェントはWordPressに組み込まれています。
コーデックスのページに記載されているように、トランジェントはプラグインをキャッシュすることで本質的に高速化されます。 データベースではなくメモリにトランジェントを保存できるためです。 一般的なルールとして、データベースに一時的なものが常に存在すると想定するべきではありません。そのため、データベースをフェッチする前に、一時的なものが存在するかどうかを確認することをお勧めします。
$transient_name = get_transient( 'transient_name' ); if ( $transient_name === false ) { set_transient( 'transient_name', $transient_data, $transient_expiry ); }
(私たちが行っているように)有効期限なしでそれを使用することができます、そしてそれが私たちがポストセーブで一種の「キャッシュバスティング」を実装した理由です。 それらが提供するすべての優れた機能に加えて、最大4GBのデータを保持できますが、単一のデータベースフィールドにこれほど大きなデータを格納することはお勧めしません。
おすすめの読み物:注意してください:サイトを安全でないものにする可能性のあるPHPおよびWordPress関数
最終エンドポイント:テストと検証
必要なパズルの最後のピースは「エンドポイント」です。 ここではエンドポイントという用語を使用していますが、結果を取得するために特定のファイルを直接呼び出しているため、エンドポイントではありません。 したがって、次のようなtest.phpファイルを作成できます。
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 ) );
https://dev.wordpress.test/wp-content/plugins/json-transient/test.php
にアクセスすると、次のメッセージが表示されます。
エラー、ページスラッグまたはタイプがありません!
したがって、投稿タイプと投稿スラッグを指定する必要があります。 https://dev.wordpress.test/wp-content/plugins/json-transient/test.php?slug=hello-world&type=post
にアクセスすると、次のように表示されます。
エラー、ページが存在しないか、正しくキャッシュされていません。 キャッシュを再構築して、もう一度やり直してください。
あ、待って! 最初にページと投稿を再保存する必要があります。 したがって、始めたばかりの場合、これは簡単です。 ただし、すでに100以上のページまたは投稿がある場合、これは困難な作業になる可能性があります。 これが、Decoupled JSON Contentプラグインでトランジェントをクリアし、それらをバッチで再構築する方法を実装した理由です。
ただし、 Hello Worldの投稿を再度保存してから、リンクを再度開いてください。 あなたが今持っているべきものはこのように見えるものです:
{ "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" }
以上です。 私たちが作成したプラグインには、使用できる追加機能がいくつかありますが、簡単に言うと、これは、RESTAPIを使用するよりもはるかに高速なWordPressからJSONデータをフェッチする方法です。
前後:応答時間の改善
Chromeでテストを実施しました。ここでは、合計応答時間とTTFBを別々に確認できました。 応答時間を10回続けてテストしました。最初はプラグインなしで、次にプラグインを追加しました。 また、投稿のリストと単一の投稿の応答をテストしました。
テストの結果を以下の表に示します。
ご覧のとおり、違いは劇的です。
セキュリティ上の懸念
注意が必要な点がいくつかあります。 まず、WordPressのコアファイルを手動でロードしています。これは、WordPressの世界では大したことではありません。 なんで? まあ、コアファイルを手動でフェッチするのは難しいという事実に加えて(特にBedrockなどの非標準のインストールを使用している場合)、セキュリティ上の懸念が生じる可能性があります。
この記事で説明されている方法を使用する場合は、サーバーのセキュリティを強化する方法を知っていることを確認してください。
まず、 test.phpファイルのようにHTMLヘッダーを追加します。
header( 'Access-Control-Allow-Origin: your-front-end-app.url' ); header( 'Content-Type: application/json' );
最初のヘッダーは、CORSセキュリティ対策をバイパスする方法であり、指定されたファイルに移動するときにフロントエンドアプリのみがコンテンツをフェッチできるようにします。
次に、アプリのディレクトリトラバーサルを無効にします。 これを行うには、 nginx
設定を変更するか、Apacheサーバーを使用している場合はOptions -Indexes
を.htaccessファイルに追加します。
応答にトークンチェックを追加することも、不要なアクセスを防ぐための優れた手段です。 私たちは実際に、これらのセキュリティ対策をデフォルトで含めることができるように、分離されたJSONプラグインを変更する方法に取り組んでいます。
フロントエンドアプリによって送信されるAuthorizationヘッダーのチェックは次のようになります。
if ( ! isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { return; } $auth_header = $_SERVER['HTTP_AUTHORIZATION'];
次に、特定のトークン(フロントエンドアプリとバックエンドアプリでのみ共有されるシークレット)が提供され、正しいかどうかを確認できます。
結論
REST APIは、データの作成、取得、更新、削除など、本格的なアプリの作成に使用できるため、優れています。 それを使用することの欠点は、その速度です。
明らかに、アプリの作成は従来のWebサイトの作成とは異なります。 インストールしたすべてのプラグインはおそらく必要ありません。 ただし、表示目的でデータが必要な場合は、データをキャッシュしてカスタムファイルで提供することは、分離されたサイトで作業する場合、現時点では完璧なソリューションのように思われます。
ウェブサイトの応答時間を短縮するためにカスタムプラグインを作成するのはやり過ぎだと思われるかもしれませんが、私たちは毎秒が重要な世界に住んでいます。 ウェブサイトが遅い場合、ユーザーはそれを放棄することを誰もが知っています。 ウェブサイトのパフォーマンスとコンバージョン率の関係を示す多くの研究があります。 しかし、それでも説得力が必要な場合、Googleは遅いウェブサイトにペナルティを科します。
この記事で説明する方法は、WordPress REST APIで発生する速度の問題を解決し、分離されたWordPressプロジェクトで作業するときにさらにブーストを提供します。 すべての要求と応答から最後の1ミリ秒を絞り出すという終わりのない探求に取り組んでいるため、プラグインをさらに最適化する予定です。 それまでの間、分離されたWordPressの高速化に関するアイデアを共有してください。