サードパーティの JavaScript を読み込む

Addy Osmani
Addy Osmani
Arthur Evans

コードを最適化してもサイトの読み込みが遅すぎる場合は、サードパーティのスクリプトが原因である可能性があります。

サードパーティ スクリプトには、ウェブをより動的でインタラクティブに、相互接続させる便利な機能が数多く用意されています。中にはウェブサイトの機能や収益源にとっても不可欠なものもあれば、ただし、これを使用することにはリスクがあります。

  • サイトのパフォーマンスが低下する可能性があります。
  • プライバシーセキュリティに関する問題を引き起こす可能性があります。
  • 動作は予測不可能で、その動作は予期しない結果をもたらす可能性があります。

サードパーティのスクリプトがサイトのクリティカル レンダリング パスに影響しないようにするのが理想的です。このガイドでは、サードパーティの JavaScript の読み込みに関連する問題を特定して修正し、ユーザーに対するリスクを最小限に抑える方法について説明します。

第三者スクリプトとは

サードパーティの JavaScript とは、多くの場合、サードパーティ ベンダーがサイトに直接埋め込めるスクリプトを指します。次に例を示します。

  • ソーシャル共有ボタン(Facebook、X、LinkedIn、Mastodon)

  • 動画プレーヤーの埋め込み(YouTube、Vimeo)

  • 広告 iframe

  • 分析と指標のスクリプト

  • テスト用の A/B テスト スクリプト

  • ヘルパー ライブラリ(日付形式、アニメーション、関数ライブラリなど)

YouTube 動画の埋め込みの例
次のコードを使用してページに追加できる YouTube 動画の埋め込みの例。
<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/embed/mo8thg5XGV0"
  frameborder="0"
  allow="autoplay; encrypted-media"
  allowfullscreen
>
</iframe>

残念ながら、サードパーティのスクリプトを埋め込む場合、私たちは多くの場合、ページ速度を低下させずに、迅速に実行するためにそれらに依存します。サードパーティ スクリプトは、サイト所有者が管理できないリソースが原因でパフォーマンスが低下する一般的な原因です。次のような問題があります。

  • 複数のサーバーに送信するネットワーク リクエストが多すぎる。サイトで必要なリクエストが多いほど、読み込みに時間がかかります。

  • JavaScript が多すぎるため、メインスレッドがビジー状態になるJavaScript が多すぎると DOM 構築がブロックされ、ページのレンダリングが遅れる可能性があります。CPU 使用率の高いスクリプトの解析と実行により、ユーザー操作が遅れ、バッテリーの消耗が早まる可能性があります。

  • 最適化されていない大きな画像ファイルや動画を送信すると、データを消費し、ユーザーの費用が発生する可能性があります。

  • ページでスクリプトが慎重に読み込まれる場合に、単一障害点(SPOF)として機能するセキュリティ上の問題。

  • HTTP キャッシュが不十分なため、リソースを取得するためにブラウザが追加のネットワーク リクエストを送信する必要があります。

  • サーバーの圧縮が不十分な場合、リソースの読み込みが遅くなります。

  • ブロックしたコンテンツは、処理が完了するまで表示されます。これは、非同期 A/B テスト スクリプトの場合も同様です。

  • document.write() など、ユーザー エクスペリエンスを損なうことが知られている従来の API の使用。

  • 過剰な DOM 要素や負荷の高い CSS セレクタがある。

  • 複数のサードパーティの埋め込みを含めると、複数のフレームワークとライブラリが複数回 pull され、リソースが浪費され、既存のパフォーマンスの問題が悪化する可能性があります。

  • サードパーティ スクリプトは、たとえ埋め込みが async または defer を使用していても、サーバーの応答が遅い場合に window.onload をブロックする可能性のある埋め込み手法を使用することがあります。

サードパーティのスクリプトの問題を修正できるかどうかは、サイトや、サードパーティのコードの読み込み方法を設定する能力によって異なります。幸いなことに、サードパーティ リソースの問題を見つけて修正するためのソリューションやツールが多数存在します。

ページ上のサードパーティのスクリプトを識別するにはどうすればよいですか?

サイト上のサードパーティ スクリプトを特定し、それがパフォーマンスに与える影響を判断することが、スクリプトの最適化への第一歩です。Chrome DevToolsPageSpeed InsightsWebPageTest などの無料のウェブ速度テストツールを使用して、コストのかかるスクリプトを特定することをおすすめします。これらのツールには豊富な診断情報が表示され、サイトで使用しているサードパーティ スクリプトの数や、実行に時間がかかっているスクリプトを確認できます。

WebPageTest のウォーターフォール ビューでは、サードパーティ スクリプトを多用している場合の影響を強調できます。Tags Gone Wild の次の画像は、トラッキングとマーケティングのスクリプトではなく、サイトのメイン コンテンツを読み込むために必要なネットワーク リクエストの例を示しています。

実際のウェブサイトと
トラッキングスクリプトの読み込みにかかった時間の
このページのスクリプトは、ページ自体よりも読み込みに時間がかかります。

WebPageTest のドメインの内訳は、サードパーティ送信元からのコンテンツ量を可視化する場合にも役立ちます。これは、合計バイト数とリクエスト数の両方を基準に分類されます。

ドメイン別のコンテンツの内訳(最初の表示)。
各サードパーティのリクエストとバイト数の割合が表示されます
ドメインの内訳グラフは、ページのコンテンツがサードパーティによってどの程度占められているかを示します。

第三者スクリプトがページに与える影響を測定するにはどうすればよいですか?

問題の原因となっているスクリプトを特定したら、そのスクリプトの動作を確認し、サイトの動作に必要かどうかを判断します。必要に応じて、A/B テストを実施して、認識される価値と、主要なユーザー エンゲージメントまたはパフォーマンス指標への影響のバランスを取ります。

Lighthouse の起動時間の監査

Lighthouse の JavaScript 起動時間監査では、スクリプトの解析、コンパイル、評価にコストがかかるスクリプトがハイライト表示されます。これは、CPU 使用率の高いサードパーティ スクリプトを特定するのに役立ちます。

スクリプトの評価と解析のサポートを示す
Lighthouse
起動時間の監査では、読み込みに最も時間がかかるスクリプトを確認できます。

Lighthouse ネットワーク ペイロードの監査

Lighthouse のネットワーク ペイロードの監査では、ネットワーク リクエストを特定します。これには、ページの読み込み速度を低下させ、ユーザーのモバイルデータ使用量を予想以上に増やす第三者ネットワーク リクエストも含まれます。

大規模なネットワーク ペイロードのサポートを示す Lighthouse
ネットワーク ペイロード監査では、最も時間がかかり、最も多くのデータを取得しているネットワーク リクエストを確認できます。

Chrome DevTools のネットワーク リクエストのブロック

Chrome DevTools を使用すると、指定したスクリプト、スタイルシート、その他のリソースが利用できない場合のページの動作を確認できます。これは、ネットワーク リクエストのブロックを使用して行います。この機能では、ページから個々のサードパーティ リソースを削除した場合の影響を測定できます。

リクエストのブロックを有効にするには、[Network] パネルで任意のリクエストを右クリックして、[Block Request URL] を選択します。DevTools ドロワーに [リクエストのブロック] タブが表示され、どのリクエストをブロックしたかを管理できます。

DevTools のネットワーク パネルでリクエスト URL をブロックする
個々のネットワーク リクエストをブロックして、そのリクエストなしでページがどのように動作するかを確認します。

Chrome DevTools の [Performance] パネル

Chrome DevTools の [パフォーマンス] パネルは、ページのウェブ パフォーマンスの問題を特定するのに役立ちます。

  1. [Record] をクリックします。
  2. ページを読み込みます。DevTools に、サイトの読み込み時間の経過を示すウォーターフォール図が表示されます。
  3. パフォーマンス パネルの下部にある [ボトムアップ] に移動します。
  4. [プロダクト別にグループ化] をクリックし、ページのサードパーティ スクリプトを読み込み時間で並べ替えます。
(サードパーティ)プロダクト別にグループ化されたボトムアップ ビューを表示している DevTools の [Performance] パネル
サードパーティ スクリプトをプロダクトで並べ替え、読み込み時間が長いものから順に並べます。

Chrome DevTools を使用してページ読み込みのパフォーマンスを分析する方法について詳しくは、ランタイム パフォーマンスの分析を使ってみるをご覧ください。

サードパーティ スクリプトの影響を測定するための推奨ワークフローは次のとおりです。

  1. [Network] パネルを使用して、ページの読み込みにかかる時間を測定します。
    • 実際の状態をエミュレートするには、ネットワーク スロットリングCPU スロットリングを有効にすることをおすすめします。ラボでは、高価なスクリプトによる影響を軽減できる高速なネットワーク接続とデスクトップ ハードウェアをユーザーが使用する可能性はまずありません。
  2. 問題があると思われるサードパーティのスクリプトの URL またはドメインをブロックします(コストのかかるスクリプトを特定する方法については、Chrome DevTools のパフォーマンス パネルをご覧ください)。
  3. ページを再読み込みして、読み込み時間を再度測定します。
    • データの精度を高めるには、読み込み時間を 3 回以上測定することをおすすめします。これは、ページの読み込みのたびに異なるリソースを取得する一部のサードパーティ スクリプトを考慮するためです。そのため、DevTools のパフォーマンス パネルでは複数の録画がサポートされています。

WebPageTest でサードパーティ スクリプトの影響を測定する

WebPageTest では、個々のリクエストの読み込みをブロックして、その影響を [詳細設定] > [ブロック] で測定できます。この機能を使用して、ブロックするドメイン(広告ドメインなど)のリストを指定します。

WebPageTest の詳細設定 > [ブロック] を選択します。
ブロックするドメインを指定するためのテキスト領域を表示します。
ブロックするドメインをこのパネルでリストする

この機能を使用するには、次のワークフローをおすすめします。

  1. サードパーティをブロックせずにページをテストします。
  2. 一部のサードパーティをブロックしてテストを繰り返します。
  3. テスト履歴から 2 つの結果を選択します。
  4. [Compare] をクリック
WebPageTest で 2 つのレポートを
比較できる比較オプションを表示
負荷テストの結果を選択して比較する。

次の画像は、有効なサードパーティ リソースの有無にかかわらず、ページの読み込み順序を比較した WebPageTest のフィルムストリップ機能を示しています。ページのパフォーマンスに大きく影響するドメインを特定するために、個々のサードパーティ配信元のテストでこの確認を行うことをおすすめします。

WebPageTest のフィルムストリップで
サードパーティを無効にした場合と
サードパーティ リソースをブロックした場合の影響(Andy Davies 著「 Using WebPageTest To Measure Impact OfThird-Party Tags」)

WebPageTest では、ドメインをブロックするために DNS レベルで動作する 2 つのコマンドもサポートしています。

  • blockDomains は、ブロックするドメインのリストを受け取ります。
  • blockDomainsExcept は、ドメインのリストを受け取り、リストに含まれないものをすべてブロックします。

WebPageTest には単一障害点(SPOF)タブもあり、タイムアウトのシミュレーションやリソース読み込みの失敗の完了を行うことができます。ドメイン ブロックとは異なり、SPOF はゆっくりとタイムアウトします。そのため、サードパーティ サービスが高負荷状態にあるときや、一時的に利用できないときに、ページがどのように動作するかをテストできます。

WebPageTest の詳細設定 > SPOF > ホストの失敗
SPOF テスト機能を使用して、指定したドメインの障害をシミュレートします。

長いタスクを使用してコストの高い iframe を検出する

サードパーティの iframe のスクリプトの実行に時間がかかると、メインスレッドがブロックされ、他のタスクが遅延する可能性があります。タスクの処理に時間がかかると、イベント ハンドラの動作が遅くなったり、フレームが低下したりして、ユーザー エクスペリエンスが低下する可能性があります。

Real User Monitoring(RUM)の長時間タスクを検出するには、JavaScript PerformanceObserver API を使用してロングタスク エントリを監視します。これらのエントリには、長いタスクの原因となったフレーム コンテキストを特定するために使用できるアトリビューション プロパティが含まれています。

次のコードは、longtask エントリをコンソールに記録します。「高価な」iframe のログエントリが記録されます。

<script>
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Attribution entry including "containerSrc":"https://example.com"
      console.log(JSON.stringify(entry.attribution));
    }
  });

  observer.observe({entryTypes: ['longtask']});
</script>

<!-- Imagine this is an iframe with expensive long tasks -->
<iframe src="https://example.com"></iframe>

長時間タスクのモニタリングの詳細については、ユーザー中心のパフォーマンス指標をご覧ください。

第三者のスクリプトを効率的に読み込むにはどうすればよいですか?

サードパーティのスクリプトによってページの読み込みが遅くなっている場合、パフォーマンスを向上させる方法はいくつかあります。

  • ドキュメントの解析がブロックされないように、async 属性または defer 属性を使用してスクリプトを読み込みます。
  • サードパーティのサーバーの動作が遅い場合は、スクリプトを自己ホストすることを検討してください。
  • スクリプトによってサイトに明確な価値が追加されない場合は、削除します。
  • <link rel=preconnect><link rel=dns-prefetch> などのリソースヒントを使用して、サードパーティのスクリプトをホストするドメインの DNS ルックアップを行います。

async または defer を使用します。

JavaScript の実行がパーサー ブロックになっています。ブラウザでスクリプトが検出されると、DOM 構築を続行する前に、DOM 構築を一時停止し、スクリプトを JavaScript エンジンに渡して、スクリプトを実行できるようにする必要があります。

async 属性と defer 属性を使用すると、この動作は次のように変更されます。

  • async は、ブラウザが HTML ドキュメントの解析を続けながら、スクリプトを非同期的にダウンロードします。スクリプトのダウンロードが完了すると、スクリプトの実行中に解析がブロックされます。

  • defer は、ブラウザが HTML ドキュメントの解析を続けながらスクリプトを非同期でダウンロードし、ドキュメントの解析が完了するまでスクリプトの実行を待機します。

クリティカル レンダリング パスでスクリプトが必要な場合を除き、サードパーティのスクリプトには、常に async または defer を使用します。一部の分析スクリプトなど、読み込みプロセスの早い段階でスクリプトを実行する必要がある場合は、async を使用します。重要性の低いリソース(ユーザーが最初に目にするページよりページ下部にレンダリングされる動画など)には defer を使用します。

パフォーマンスが最優先事項である場合は、ページの重要なコンテンツが読み込まれるまで非同期スクリプトを追加しないことをおすすめします。jQuery などの必須ライブラリに async を使用することはおすすめしません。

一部のスクリプト(特にサイトの重要な部分)は、async または defer を使用せずに読み込む必要があります。これには、サイトが機能しない UI ライブラリやコンテンツ配信ネットワーク(CDN)フレームワークが含まれます。

他のスクリプトは、非同期で読み込まれると機能しません。ドキュメントで使用中のスクリプトを確認し、非同期で読み込めないスクリプトがある場合は、可能な別のスクリプトに置き換えます。一部のサードパーティは、非同期的に同じように動作する場合でも、スクリプトを同期的に実行することを推奨しています。

async ですべてが修正されるわけではありません。ページに多数のスクリプトが含まれている場合(広告目的のトラッキング スクリプトなど)、それらを非同期で読み込んでもページの読み込み速度は低下しません。

リソースヒントを使用して接続設定時間を短縮する

ネットワーク リクエストには DNS ルックアップやリダイレクトなど複数の複雑なコンポーネントがあるため、サードパーティの送信元への接続を確立するには時間がかかります。低速のネットワークでは特に、この接続の確立に時間がかかることがあります。 などのリソースヒントを使用して、ページ読み込みプロセスの早い段階でサードパーティ スクリプトをホストしているドメインの DNS ルックアップを実行できます。これにより、残りのネットワーク リクエストをより迅速に処理できます。

<link rel="dns-prefetch" href="http://example.com" />

接続先のサードパーティ ドメインが HTTPS を使用している場合は、 を使用することもできます。これは、DNS ルックアップの実行、TCP ラウンドトリップの解決、TLS ネゴシエーションの処理の両方を行います。他の手順には SSL 証明書の検証が含まれるため、非常に時間がかかることがあります。また、事前接続によって読み込み時間を大幅に短縮できます。

<link rel="preconnect" href="https://cdn.example.com" />

iframe を使用する「サンドボックス」スクリプト

サードパーティのスクリプトを iframe に直接読み込む場合は、メインページの実行はブロックされません。AMP はこの方法を使用して、JavaScript をクリティカル パスから外します。この方法でも onload イベントはブロックされるため、重要な機能を onload に接続しないようにしてください。

Chrome は権限ポリシー(旧称「機能ポリシー」)もサポートしています。これは、デベロッパーが特定のブラウザ機能へのアクセスを選択的に無効にできるようにする一連のポリシーです。これにより、サードパーティのコンテンツによってサイトに望ましくない動作が導入されるのを防ぐことができます。

自己ホストのサードパーティ スクリプト

DNS 時間の短縮や HTTP キャッシュ ヘッダーの改善など、重要なスクリプトの読み込み方法をより細かく制御する必要がある場合は、自分でホストできます。

ただし、自己ホスティングにはそれ特有の問題が伴います。特にスクリプトを更新する場合です。自己ホスト型スクリプトの場合、API の変更やセキュリティ修正の自動更新は行われません。スクリプトを手動で更新するまでは、収益の損失やセキュリティの問題につながる可能性があります。

別の方法として、Service Worker を使用してサードパーティのスクリプトをキャッシュに保存すると、ネットワークからスクリプトを取得する頻度をより細かく制御できます。また、Service Worker を使用して、ページが主要なユーザー操作に到達するまで、不要なサードパーティのリクエストを抑制する読み込み戦略を作成することもできます。

少数のユーザー サンプルで A/B テストを実施

A/B テスト(スプリット テスト)は、ページの 2 つのバージョンをテストし、ユーザー エクスペリエンスと動作を分析する手法です。これにより、ウェブサイトのトラフィックのさまざまなサンプルでそのページのバージョンを利用できるようになり、どちらのバージョンの方がコンバージョン率が高いかをアナリティクスで判断できます。

ただし、A/B テストは設計上、有効化する必要があるテストを判断するためにレンダリングを遅延します。JavaScript は、A/B テストに含まれるユーザーがいるかどうかを確認し、適切なパターンを有効にするためによく使用されます。このプロセスは、テストに参加していないユーザーであっても、エクスペリエンスを悪化させる可能性があります。

ページのレンダリングを高速化するには、少数のユーザーベースのサンプルに A/B テスト スクリプトを送信し、サーバーサイドで表示するページのバージョンを決定するコードを実行することをおすすめします。

サードパーティ リソースの遅延読み込み

広告や動画などのサードパーティの埋め込みリソースは、構成が不適切だとページの読み込み速度が遅くなる主な原因となります。遅延読み込みを使用すると、埋め込みリソースを必要な場合にのみ読み込むことができます。たとえば、ユーザーがページをスクロールし、十分な距離までスクロールするまで、ページのフッターに広告が配信されるのを待つなどです。メインページ コンテンツが読み込まれた後、ユーザーがページを操作する前に、サードパーティのコンテンツを遅延読み込みすることもできます。

スクロールせずに見える範囲に不可欠なアセットと、重要性が低く遅延読み込み可能なアセットを示す図。
ページの読み込み後すぐにはユーザーに表示されないアセットを遅延読み込みできます。

リソースの遅延読み込みには JavaScript コードが含まれていることが多く、不安定なネットワーク接続の影響を受ける可能性があるため、注意が必要です。

DoubleClick の公式ドキュメントに、広告を遅延読み込みする方法が記載されています。

Intersection Observer による効率的な遅延読み込み

従来、遅延読み込みを目的としてビューポートに要素が表示されているかどうかを検出するメソッドはエラーが発生しやすく、多くの場合、ブラウザの処理速度を低下させてきました。このような非効率なメソッドは多くの場合、scroll または resize イベントをリッスンし、getBoundingClientRect() などの DOM API を使用して、ビューポートに対する要素の相対位置を計算します。

IntersectionObserver は、観測された要素がブラウザのビューポートを出入りしたことをページ所有者が効率的に検出できるようにするブラウザ API です。LazySizes には IntersectionObserver のオプションのサポートもあります。

遅延読み込みの分析

分析スクリプトの読み込みを長時間延期すると、貴重な分析データを見逃す可能性があります。幸いなことに、ページ読み込みの初期データを保持しながら、分析を遅延なく初期化できる戦略があります。

Phil Walton 氏のブログ投稿「The Google Analytics Setup I Use on All Site I Build」(開発するすべてのサイトで使っている Google アナリティクスの設定)では、Google アナリティクスの戦略を取り上げています。

サードパーティ スクリプトを安全に読み込む

このセクションでは、サードパーティ スクリプトをできるだけ安全に読み込むためのガイダンスを提供します。

document.write() を避ける

サードパーティ スクリプト(特に古いサービスの場合)は、document.write() を使用してスクリプトの挿入と読み込みを行うことがあります。document.write() の動作に一貫性がなく、その障害のデバッグが困難であるため、これは問題です。

document.write() の問題を修正するには、この関数を使用しないでください。Chrome 53 以降では、document.write() の使用に関する問題があると、Chrome DevTools によって警告がコンソールに記録されます。

DevTools コンソールの警告で、document.write() を使用したサードパーティ埋め込みの違反がハイライト表示される
Chrome DevTools で document.write() の使用状況にフラグが立てられる

このエラーが発生した場合は、ブラウザに送信された HTTP ヘッダーを探すことで、サイトで document.write() が使用されているかどうかを確認できます。Lighthouse では、まだ document.write() を使用しているサードパーティ スクリプトもハイライト表示できます。

Lighthouse のベスト プラクティスの監査では、document.write() の使用を
報告します。
document.write() を使用しているスクリプトを示す Lighthouse のレポート

タグ マネージャーを使用する際の注意点

「タグ」とは、デジタル マーケティング チームがデータの収集、Cookie の設定、ソーシャル メディア ウィジェットなどのサードパーティ コンテンツのサイトへの統合を行うためのコード スニペットです。これらのタグを使用すると、ネットワーク リクエスト、JavaScript 依存関係、その他のリソースがページに追加され、ページのパフォーマンスに影響を与える可能性があります。タグが追加されるほど、ユーザーへの影響を最小限に抑えることは難しくなります。

ページの読み込みを速くするには、Google タグ マネージャー(GTM)などのタグ マネージャーを使用することをおすすめします。GTM を使用すると、タグ同士が非同期的にデプロイされるため、タグ同士の読み込みが妨げられることがなくなります。また、ブラウザでタグを実行するために必要なネットワーク呼び出しの回数が減り、データレイヤー UI でタグデータが収集されます。

タグ マネージャーを使用する際のリスク

タグ マネージャーはページの読み込みを効率化するように設計されていますが、不用意に使用した場合、以下のように動作が遅くなることがあります。

  • タグ マネージャーに過剰な数のタグや自動イベント リスナーが含まれていると、必要以上に多くのネットワーク リクエストがブラウザから送信され、コードのイベントへの迅速な応答が難しくなります。
  • 認証情報とアクセス権を持つユーザーであれば誰でも、タグ マネージャーに JavaScript を追加できます。これにより、ページの読み込みに必要なネットワーク リクエストの数が増えるだけでなく、不要なスクリプトによるセキュリティ リスクやその他のパフォーマンスの問題を引き起こす可能性もあります。こうしたリスクを軽減するため、タグ マネージャーへのアクセスを制限することをおすすめします。

グローバル スコープを汚染するスクリプトを避ける

サードパーティ スクリプトは、次のようなさまざまな動作をする可能性があり、ページの動作が予期せず中断することがあります。

  • JavaScript の依存関係を読み込むスクリプトは、コードと正しくやり取りしないコードによってグローバル スコープを汚染する可能性があります。
  • 予期しない更新により、互換性を破る変更が発生する可能性があります。
  • サードパーティのコードは、ページのテストとデプロイの間で異なる動作をするように転送中に変更できます。

読み込むサードパーティのスクリプトを定期的に監査して、不正な行為者がないか確認することをおすすめします。また、セルフテスト、サブリソースの整合性、サードパーティのコードの安全な送信を実装して、ページを安全に保つこともできます。

緩和戦略

サードパーティ スクリプトがサイトのパフォーマンスやセキュリティに及ぼす影響を最小限に抑えるための大規模な戦略をいくつか紹介します。

次の例は、CSP の script-src ディレクティブを使用して、ページで使用できる JavaScript ソースを指定する方法を示しています。

// Given this CSP header Content-Security-Policy: script-src
https://example.com/ // The following third-party script will not be loaded or
executed

<script src="https://not-example.com/js/library.js"></script>

関連情報

サードパーティの JavaScript の最適化について詳しくは、以下をお読みください。

レビューしてくれた Kenji Baheux、Jeremy Wagner、Pat Meenan、Philip Walton、Jeff Posnick、Cheney Tsai に感謝します。