WordPressのJavaScriptは地獄です…そしてこれが理由です

公開: 2022-05-05

WordPressの開発スタックは近年大きく変化しています。 グーテンベルクの登場以来、私たちのお気に入りのCMSにおけるJavaScriptの役割はこれまで以上に重要になっています。 このブログでは、これが開発者にもたらす利点についてすでに詳しく説明しました(いくつか例を挙げると、Gutenbergの拡張機能、TypeScriptとReactに関するアドバイス、開発ツール、プラグインの例などについて説明しました)が、ストーリーには暗い面があります…そしてそれが今日ここで話します。

今日の投稿では、WordPressプラグイン開発者であるあなたが将来直面する可能性のある3つの主な問題をあなたと共有します。 そして面白い部分は、それらのそれぞれが異なる犯人を持っているということです:WordPress自体、他の開発者、またはあなた自身。 そこで、ここで説明します。発生する可能性のある最も一般的なJavaScriptの頭痛の種と、それらを回避/修正するためにできることです。

あなたのプラグインとあなたのサイトを壊す#1WPOプラグイン

ここNelioでたくさんのチケットの原因となっている問題から始めましょう:WPOプラグイン。

読み込みが速い軽量のウェブサイトを持つことの重要性を概説した記事やブログ投稿をたくさん読んだと思います。 つまり、私たちはそれについて何度か書いています! 彼らが通常提供するヒントの中には、より良いホスティングプロバイダーへの切り替え、キャッシュプラグインとCDNの使用、サーバーとWordPressの最新の状態の維持、または(そしてここで最初の問題が発生する)WPOプラグインのインストールなどがあります。 後者の例としては、次のものがあります。

  • インストール数が100万を超えるW3トータルキャッシュ
  • インストール数が100万を超えるSiteGroundOptimizer(ちなみに、そのうちの1つは当社のWebサイトで使用しています)
  • 仲良しのFernandoTelladoによるWordPressWPOの調整と最適化

これらのプラグインは、一般的に「どのWordPressサイトでも恩恵を受けることができる」一連の賢明な最適化を通じて、Webサイトを高速化することを約束します。 これらの最適化には次のものが含まれます。

  • 絵文字やDashiconsなどのフロントエンドで不要なスクリプトをデキューする
  • ページとデータベースクエリのキャッシュ
  • ヘッダーに含まれる情報の量を減らす
  • JavaScriptスクリプトとCSSスタイルの組み合わせと縮小
  • HTMLの縮小
  • 静的アセットのURLからバージョンクエリ引数を削除する
  • JavaScriptスクリプトを延期したり、非同期で読み込んだりする

私が言ったように、これらのタイプの最適化は、一般的に有益かもしれません。 しかし、私たちの経験では、WordPress WebサイトでのすべてのJS最適化は、より多くの問題を引き起こす傾向があるため、想定される改善は役に立たなくなります。 私が何年にもわたって見た実際の例は次のとおりです。

  • スクリプトを組み合わせる。 ブラウザが要求する必要のあるスクリプトが少ないほど、優れています。 そのため、すべてのスクリプトを1つにまとめることが理にかなっています。 ただし、これは問題になる可能性があります。 一般に、JavaScriptスクリプトがクラッシュすると、その実行は終了し、エラーはブラウザのコンソールに報告されます。 ただし、そのスクリプトの実行のみが停止されます。 他のスクリプトは正常に実行されます。 しかし、それらをすべて組み合わせると…まあ、1つのスクリプトが失敗するとすぐに、他のスクリプト(おそらくあなたのスクリプトを含む)は実行されず、ユーザーはそれがあなたのプラグインであると思います。
  • スクリプトの縮小。 信じられないかもしれませんが、正規表現が壊れて構文エラーのあるJSスクリプトが生成されるミニファイプロセスを見てきました。 確かに、前回これに出会ってから久しぶりですが…:-/
  • クエリ引数。 スクリプトをWordPressにエンキューするときは、そのバージョン番号を使用してエンキューできます(ちなみに、これはおそらく@wordpress/scriptsによって自動生成されたものです)。 バージョン番号は非常に役立ちます。プラグインを更新してスクリプトを変更した場合、この新しいバージョン番号により、すべての訪問者に異なるURLが表示されるため、ブラウザが新しいバージョンを要求します。 残念ながら、WPOプラグインがクエリ文字列を削除すると、訪問者はスクリプトが変更されたことに気付かず、そのスクリプトのキャッシュされたコピーを使用する可能性があります。これにより、意図しない結果が生じる場合と生じない場合があります。 くそ!

完全な災害ですね。 ただし、次の音が聞こえるまで待ちます。

スクリプトの延期

Nelioでは、訪問者を追跡し、どのデザインとコンテンツが最も多くのコンバージョンを獲得しているかを発見するためのA/Bテストプラグインを実装しました。 ご想像のとおり、追跡スクリプトは次のようになります。

 window.NelioABTesting = window.NelioABTesting || {}; window.NelioABTesting.init = ( settings ) => { // Add event listeners to track visitor events... console.log( settings ); };

つまり、現在実行中のテストについてスクリプトに通知するために呼び出す必要のあるinit関数を公開します。 このメソッドを呼び出すために、次のようにPHPでインラインスクリプトをキューに入れます。

 function nab_enqueue_tracking_script() { wp_enqueue_script( 'nab-tracking', ... ); wp_add_inline_script( 'nab-tracking', sprintf( 'NelioABTesting.init( %s );', wp_json_encode( nab_get_tracking_settings() ) ) ); } add_action( 'wp_enqueue_scripts', 'nab_enqueue_tracking_script' );

その結果、次のHTMLタグが作成されます。

 <head> ... <script type="text/javascript" src="https://.../dist/tracking.js" ></script> <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ...

しかし、WPOプラグインがdefer属性をスクリプトに追加するとどうなりますか?

 <head> ... <script defer <!-- This delays its loading... --> type="text/javascript" src="https://.../dist/tracking.js" ></script> <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ...

さて、スクリプトは延期されました…これは前のスニペットがこれと同等であることを意味します:

 <head> ... <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ... <script type="text/javascript" src="https://.../dist/tracking.js" ></script> </body> </html>

その結果、 nab-tracking-jsは想定どおりに読み込まれなくなったため、その後に続くインラインスクリプトは単に失敗します。nab nab-tracking-js-afterNelioABTesting.initを使用します。 、 deferディレクティブのおかげで、まだ利用できません。 最悪!

解決

最も効果的な解決策は明らかです。スクリプトの最適化を無効にして、それを1日と呼ぶようにユーザーに指示します。 結局のところ、JavaScriptでの依存関係の管理は、一般に非常に複雑であり(特に、 deferおよびasyncディレクティブを使用する場合)、WordPressも例外ではありません。 このトピックに関するこの12年前のディスカッションをご覧ください。

しかし、それが実行可能でない場合(そしてそれが実行可能でないことを私は知っています)、私はあなたが私たちがしたのと同じことをすることをお勧めします: initメソッドを取り除き、あなたの通常のインラインスクリプトの責任を逆にします。 つまり、通常のスクリプトのにインラインスクリプトを追加し、それを使用して、require設定でグローバル変数を定義します。

 function nab_enqueue_tracking_script() { wp_enqueue_script( 'nab-tracking', ... ); wp_add_inline_script( 'nab-tracking', sprintf( 'NelioABTestingSettings = %s;', wp_json_encode( nab_get_tracking_settings() ) ), 'before' ); } add_action( 'wp_enqueue_scripts', 'nab_enqueue_tracking_script' );

結果のHTMLは次のようになります。

 <head> ... <script type="text/javascript" > NelioABTestingSettings = {"experiments":[...],...}; </script> <script type="text/javascript" src="https://.../dist/tracking.js" ></script> ... </head> <body> ...

したがって、外部スクリプトの実行が遅延するかどうかは関係ありません。常にインラインスクリプトの後に表示されるため、外部スクリプト間の依存関係が満たされます。

最後に、誰も設定を変更しないようにする場合は、変数をconstとして宣言し、その値をObject.freezeでフリーズします。

 ... sprintf( 'const NelioABTestingSettings = Object.freeze( %s );', wp_json_encode( nab_get_tracking_settings() ) ), ...

これはすべての最新のブラウザでサポートされています。

#2WordPressの依存関係が機能する場合と機能しない場合があります…

依存関係の管理は、特にWordPressの組み込みスクリプトについて話す場合、WordPressでも問題になる可能性があります。 説明させてください。

たとえば、ここで説明したように、グーテンベルクの小さな拡張機能を作成していると想像してください。 プラグインのソースコードには、おそらく次のようなimportステートメントが含まれています。

 import { RichTextToolbarButton } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { registerFormatType } from '@wordpress/rich-text'; // ...

このJSソースコードがトランスパイルされると、Webpack(または使用するツール)はすべての依存関係と独自のソースコードを単一のJSファイルにパッケージ化します。 これは、すべてが期待どおりに機能するように、後でWordPressからエンキューするファイルです。

@wordpress/scriptsを使用してこのようなファイルを作成した場合、組み込みのプロセスではパッケージがグローバルスコープで使用可能であると想定されているため、一部の依存関係は出力ファイルに含まれません。 これは、以前のインポートが次のようなものにトランスパイルされることを意味します。

 const { RichTextToolbarButton } = window.wp.blockEditor; const { __ } = window.wp.i18n; const { registerFormatType } = window.wp.richText; // ...

スクリプトの依存関係を見逃さないようにするために、 @wordpress/scriptsはJSコードをトランスパイルするだけでなく、WordPressの依存関係を含むPHPファイルも生成します。

 <?php return array( 'dependencies' => array('wp-block-editor','wp-i18n','wp-rich-text'), 'version' => 'a12850ccaf6588b1e10968124fa4aba3', );

かなりきちんとしていますねそれで、問題は何ですか? これらのWordPressパッケージは継続的に開発されており、頻繁に変更され、新しい機能や改善が追加されています。 したがって、最新バージョンのWordPressを使用してプラグインを開発すると、その最新バージョンで使用できる機能や機能を誤って使用してしまう可能性があります(したがって、すべてが正常に機能します)が、「古い」WordPressバージョンでは使用できません…

どうやってわかりますか?

解決

ここでの私のアドバイスは非常に単純です。最新のWordPressバージョンを使用してプラグインを開発しますが、古いバージョンでリリースをテストします。 特に、少なくともプラグインがサポートするはずの最小のWordPressバージョンでプラグインをテストすることをお勧めします。 プラグインのreadme.txtにある最小バージョン:

 === Nelio Content === ... Requires PHP: 7.0 Requires at least: 5.4 Tested up to: 5.9 ...

あるWordPressバージョンから別のバージョンへの切り替えは、次のWPCLIコマンドを実行するのと同じくらい簡単です。

 wp core update --version=5.4 --force

#3矢印機能はあなたが思っているよりもトリッキーです

最後に、私がほんの数日前に遭遇し、私を夢中にさせた最新の問題の1つを共有させてください。 簡単に言うと、次のようなJavaScriptファイルがあります。

 import domReady from '@wordpress/dom-ready'; domReady( () => [ ...document.querySelectorAll( '.nelio-forms-form' ) ] .forEach( initForm ) ); // Helpers // ------- const initForm = ( form ) => { ... } // ...

これにより、フロントエンドでNelioフォームが初期化されます。 スクリプトは非常に単純ですよね? DOMの準備ができたときに呼び出される無名関数を定義します。 この関数は、 initFormと呼ばれるヘルパー(矢印)関数を使用します。 実は、このような単純な例はクラッシュする可能性があります。 ただし、特定の状況下でのみ(つまり、スクリプトがdefer属性を使用してWPOプラグインによって「最適化」された場合)。

JSが前のスクリプトを実行する方法は次のとおりです。

  1. domReady内の無名関数が定義されています
  2. domReadyが実行されます
  3. DOMの準備がまだ整っていない場合(通常、スクリプトがロードされている場合は準備ができていません)、 domReadyはコールバック関数を実行しません。 代わりに、後で呼び出すことができるように、単にそれを追跡します
  4. JavaScriptはファイルの解析を続行し、 initForm関数をロードします
  5. DOMの準備ができると、コールバック関数が最終的に呼び出されます

では、3番目のステップに到達するまでに、DOMの準備ができているため、 domReadyが無名関数を直接呼び出すとしたらどうでしょうか。 その場合、 initFormはまだundefinedであるため、スクリプトは未定義のエラーをトリガーします。

実際、これらすべてについて最も興味深いのは、これら2つのソリューションが同等であるということです。

 domReady( aux ); const aux = () => {};
 domReady( () => aux() ); const aux = () => {}

JavaScriptリンターは、最初のリンターでのみエラーをスローし、最新のリンターではエラーをスローしません。

解決

考えられる解決策は2つあります。functionキーワードを使用してヘルパーfunctionを定義してarrow関数を忘れるか、すべてのヘルパー関数を定義した後でdomReadyステートメントを最後に移動します。

 domReady( aux ); function aux() { // ... }
 const aux = () => { // ... }; domReady( aux );

最初のソリューションが元のソリューションと明らかに同等であるのになぜ機能するのか疑問に思われる場合は、JavaScriptの巻き上げがどのように機能するかがすべてです。 つまり、JavaScriptでは、定義の前に関数( functionで定義)を使用できますが、変数と定数(したがって矢印関数)で同じことを行うことはできません。

要約すれば

JavaScriptでうまくいかないことがたくさんあります。 幸いなことに、特に私たちが何をしているのかに注意を払えば、それらはすべて解決策を持っています。 今日、あなたが何か新しいことを学んだことを願っています。私が過去に犯した過ちや過ちのおかげで、将来あなた自身の肉体でそれらに苦しむことを避けることができると信じています。

UnsplashのIanStaufferによる注目の画像。