diff options
| author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
|---|---|---|
| committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
| commit | 33058f2b292b3a581333bdfb21b8f671898c5060 (patch) | |
| tree | 51c3e392513ec574331b2d3f85c394445ea803c6 /files/ja/learn/javascript/client-side_web_apis/fetching_data | |
| parent | 8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff) | |
| download | translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2 translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip | |
initial commit
Diffstat (limited to 'files/ja/learn/javascript/client-side_web_apis/fetching_data')
| -rw-r--r-- | files/ja/learn/javascript/client-side_web_apis/fetching_data/index.html | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/files/ja/learn/javascript/client-side_web_apis/fetching_data/index.html b/files/ja/learn/javascript/client-side_web_apis/fetching_data/index.html new file mode 100644 index 0000000000..44f7c8b035 --- /dev/null +++ b/files/ja/learn/javascript/client-side_web_apis/fetching_data/index.html @@ -0,0 +1,389 @@ +--- +title: サーバからのデータ取得 +slug: Learn/JavaScript/Client-side_web_APIs/Fetching_data +tags: + - API + - Article + - Beginner + - CodingScripting + - Fetch + - JSON + - JavaScript + - Learn + - Promises + - Server + - XHR + - XML + - XMLHttpRequest + - data + - request +translation_of: Learn/JavaScript/Client-side_web_APIs/Fetching_data +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div> + +<p class="summary">モダンな Web サイトやアプリケーションでしょっちゅう必要になる仕事は、サーバから個々のデータを取ってきて、新しいページ全体を読んでくることなしに、ページの一部を書き換える事です。この一見ちょっとした事が、サイトのパフォーマンスや振舞いに巨大なインパクトを与えました。この記事ではそのコンセプトを解説し、これを可能にした技術 XMLHttpRequest や Fetch API について見ていきます。</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">前提条件:</th> + <td>JavaScript の基本 (<a href="/ja/docs/Learn/JavaScript/First_steps">最初のステップ</a>、<a href="/ja/docs/Learn/JavaScript/Building_blocks">ビルディングブロック</a>、<a href="/ja/docs/Learn/JavaScript/Objects">JavaScript オブジェクト</a>を参照)、<a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">クライアントサイド API の基本</a></td> + </tr> + <tr> + <th scope="row">目標:</th> + <td>サーバからデータを取得し、それを使用して Web ページのコンテンツを更新する方法を習得する。</td> + </tr> + </tbody> +</table> + +<h2 id="これの問題は何か">これの問題は何か?</h2> + +<p>もともと Web のページ読み込みは単純でした — Web サイトのデータをサーバにリクエストすると、何も問題がなければ、ページを構成するいろいろなものがダウンロードされてあなたのコンピュータに表示されていました。</p> + +<p><img alt="A basic representation of a web site architecture" src="https://mdn.mozillademos.org/files/6475/web-site-architechture@2x.png" style="display: block; height: 134px; margin: 0px auto; width: 484px;"></p> + +<p>このモデルの問題は、どこかページの一部を書き換えたい場合、例えば新しい商品の一群を表示したり新しいページを読み込ませたりをする毎に、ページ全体を読み直さなければならない事です。これはとても無駄が多くてユーザ体験が悪化します、とりわけページが大きくて複雑になってくるにつれて。</p> + +<h3 id="Ajax_の登場">Ajax の登場</h3> + +<p>上述の問題を解決すべく、Web ページから細かいデータ (<a href="/docs/Web/HTML">HTML</a>、{{glossary("XML")}}、<a href="/docs/Learn/JavaScript/Objects/JSON">JSON</a> やプレーンテキストのような) をリクエストし、それを必要な時だけ表示するという技術の誕生へと繋がりました。</p> + +<p>これは {{domxref("XMLHttpRequest")}} や、最近では <a href="/docs/Web/API/Fetch_API">Fetch API</a> の利用によって実現されます。これらの技術は、Web ページがサーバにある特定のリソースを直接 <a href="/docs/Web/HTTP">HTTP</a> リクエストし、必要があれば結果のデータを表示する前に整形する事を可能にしました。</p> + +<div class="note"> +<p><strong>注記</strong>: これらのテクニック一般はかつて Ajax (Asynchronous JavaScript and XML)と呼ばれていましたが、これは {{domxref("XMLHttpRequest")}} を使って XML データを要求するものが多かったためです。今日ではそういうものばかりではありませんが (<code>XMLHttpRequest</code> や Fetch を使って JSON を要求する場合の方が多いでしょう)、結果としては同じであり、"Ajax" という用語はしばしば今でもこのテクニックを説明するのに使われます。</p> +</div> + +<p><img alt="A simple modern architecture for web sites" src="https://mdn.mozillademos.org/files/6477/moderne-web-site-architechture@2x.png" style="display: block; height: 235px; margin: 0px auto; width: 559px;"></p> + +<p>Ajax モデルには、ブラウザにページ全体をリロードされるのではなく、もっと賢くデータをリクエストするために Web API をプロキシとして使うという事も含まれます。これの重要性を考えてみて下さい:</p> + +<ol> + <li>お気に入りの情報に富んだサイト、アマゾンとか YouTube とか CNN とかに行って読み込みます。</li> + <li>さて新しい商品だか何だかを検索します。メインのコンテンツは変わるでしょうが、周りに表示されている情報、ヘッダーやフッター、ナビゲーションメニューなど、大半はそのままでしょう。</li> +</ol> + +<p>これはとても良いことで、それは:</p> + +<ul> + <li>ページの更新がずっと素早く、切り替わるのを待つ必要もないので、サイトがずっと早くて反応の良いものに感じられます。</li> + <li>更新毎にダウンロードされるデータが少ないので、帯域の無駄が少なくなります。ブロードバンドに接続されたデスクトップではさして問題ではないかもしれませんが、モバイルデバイスからや、どこでも高速インターネット接続が使えるわけではない開発途上国ではとても重要な問題です。</li> +</ul> + +<p>さらなる高速化のために、サイトの中には必要なものやデータを最初にリクエストされた時にユーザのコンピュータに保存してしまい、以降の訪問では保存ずみのものを、サーバから最新版のダウンロードさせる事なく使用するものもあります。コンテンツはそれが更新された時だけサーバから再読み込みされます。</p> + +<p><img alt="A basic web app data flow architecture" src="https://mdn.mozillademos.org/files/6479/web-app-architecture@2x.png" style="display: block; height: 383px; margin: 0px auto; width: 562px;"></p> + +<h2 id="基本的な_Ajax_リクエスト">基本的な Ajax リクエスト</h2> + +<p>{{domxref("XMLHttpRequest")}} と <a href="/docs/Web/API/Fetch_API">Fetch</a> それぞれを使って、そのようなリクエストをどうやるのか見ていきましょう。それらの例では、いくつかの異なるテキストファイルから取り出したデータをリクエストし、コンテンツ領域に埋め込みます。</p> + +<p>この一連のファイルは疑似データベースとして働きます。実際のアプリケーションでは、PHP や Python、Node のようなサーバサイド言語を使ってデータベースから取り出したデータをリクエストする場合が多いでしょう。ですがここでは簡単にしておき、クライアント側のパートに集中します。</p> + +<h3 id="XMLHttpRequest">XMLHttpRequest</h3> + +<p><code>XMLHttpRequest</code> (よく XHR と略記されます) は今となってはかなり古い技術です — Microsoft によって1990年代に発明され、非常に長い間ブラウザを超えて標準化されてきました。</p> + +<ol> + <li> + <p>この例題を始めるにあたり、<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/ajax-start.html">ajax-start.html</a> と4つのテキストファイル — <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse1.txt">verse1.txt</a>、<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse2.txt">verse2.txt</a>、<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse3.txt">verse3.txt</a> と <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse4.txt">verse4.txt</a> — のローカルコピーを、あなたのコンピュータの新しいディレクトリに作って下さい。この例題では、ドロップダウンメニューから選択されたら、詩 (ご存知の詩かも) のこれら異なる節を XHR を使って読み込みます。</p> + </li> + <li> + <p>{{htmlelement("script")}} 要素のすぐ内側に、下のコードを書き足して下さい。これは {{htmlelement("select")}} と {{htmlelement("pre")}} 要素への参照を定数に保存し、{{domxref("GlobalEventHandlers.onchange","onchange")}} イベントハンドラ関数を定義していて、これは select の値が変わったら、その値が呼び出される関数 <code>updateDisplay()</code> の引数となるようにします。</p> + + <pre class="brush: js notranslate">const verseChoose = document.querySelector('select'); +const poemDisplay = document.querySelector('pre'); + +verseChoose.onchange = function() { + const verse = verseChoose.value; + updateDisplay(verse); +};</pre> + </li> + <li> + <p><code>updateDisplay()</code> 関数を定義しましょう。まずはさっきのコードブロックの下に以下を書き足します — これは関数のからっぽのガワです。 注: ステップ 4 から 9 はすべて、この関数<em>内で</em>実施します。</p> + + <pre class="brush: js notranslate">function updateDisplay(verse) { + +}</pre> + </li> + <li> + <p>関数を、後から必要になる読み込みたいテキストファイルを指す相対 URL を作るところからはじめます。{{htmlelement("select")}} 要素の値は常に、選択されている {{htmlelement("option")}} の内側テキスト、例えば"Verse 1"とか、に一致します (value 属性で異なる値を設定していなければ)。これに相当するテキストファイルは "verse1.txt" で HTML と同じディレクトリにあるので、ファイル名だけで十分です。</p> + + <p>ただ、Web サーバはたいてい大文字小文字を区別しますし、今回のファイル名にスペースは含まれていません。"Verse 1" を "verse1.txt" に変換するためには、V を小文字にして、スペースを取り除き、.txt を末尾に追加しなければなりません。これは{{jsxref("String.replace", "replace()")}} に {{jsxref("String.toLowerCase", "toLowerCase()")}}、あと単なる <a href="/ja/docs/Learn/JavaScript/First_steps/Strings#Concatenating_strings">文字列の結合</a> で実現できます。以下のコードをあなたの <code>updateDisplay()</code> 関数の内側に追加して下さい:</p> + + <pre class="brush: js notranslate">verse = verse.replace(" ", ""); +verse = verse.toLowerCase(); +let url = verse + '.txt';</pre> + </li> + <li> + <p>XHR リクエストを作り始めるため、リクエストオブジェクトを {{domxref("XMLHttpRequest.XMLHttpRequest", "XMLHttpRequest()")}} コンストラクタを使って作成しなければなりません。このオブジェクトには好きな名前を付けられますが、単純にするため <code>request</code> を使います。<code>updateDisplay()</code> 関数の内側で、先の行の下に以下を追加します:</p> + + <pre class="brush: js notranslate">let request = new XMLHttpRequest();</pre> + </li> + <li> + <p>次に {{domxref("XMLHttpRequest.open","open()")}} メソッドを使ってどの <a href="/ja/docs/Web/HTTP/Methods">HTTP リクエストメソッド</a> を使ってリソースをネットワークから取得するか、URL はどこかを指定しなければなりません。ここでは単に <code><a href="/ja/docs/Web/HTTP/Methods/GET">GET</a></code> メソッドを使い、URL には <code>url</code> 変数の値をセットします。先の行の下に以下を追加します:</p> + + <pre class="brush: js notranslate">request.open('GET', url);</pre> + </li> + <li> + <p>次はレスポンスにどのような形式にしたいか指定 — これはリクエストの {{domxref("XMLHttpRequest.responseType", "responseType")}} プロパティで指定します — <code>text</code> にします。厳密に言えばこの場合は必須の指定ではありません — XHR はデフォルトで text を返します — が、いつの日か他のデータ形式を指定したくなる場合にそなえて、この設定をする習慣をつけておくと良いと思います。次を追加して下さい:</p> + + <pre class="brush: js notranslate">request.responseType = 'text';</pre> + </li> + <li> + <p>ネットワークからリソースを取得する処理は非同期{{glossary("asynchronous")}} 処理なので、戻りを使って何かをする前に、あなたは処理が完了(リソースがネットワークから返ってくる)するのを待たなければならず、さもないとエラーが投げられます。XHR では {{domxref("XMLHttpRequest.onload", "onload")}} イベントハンドラを使ってこの問題をさばけます — これは {{event("load")}} イベントが発火(レスポンスが返ってきた)した時に実行されます。このイベントが起きた後は、レスポンスデータは XHR リクエストオブジェクトの <code>response</code> プロパティとして取得できます。</p> + + <p>さっき追加した行の後に以下を追加して下さい。<code>onload</code> イベントハンドラの中で、<code>poemDisplay</code> ({{htmlelement("pre")}}要素) の <code><a href="/ja/docs/Web/API/Node/textContent">textContent</a></code> プロパティに {{domxref("XMLHttpRequest.response", "request.response")}} プロパティの値を設定しているのがお判りでしょう。</p> + + <pre class="brush: js notranslate">request.onload = function() { + poemDisplay.textContent = request.response; +};</pre> + </li> + <li> + <p>以上は全部、XHR リクエストの設定です — 実は私たちがやれと指示するまで動作はしません。やれと指示するには、{{domxref("XMLHttpRequest.send","send()")}} メソッドを使います。さっき追加した行の後に以下を追加して、関数を完成させます。この行は、<code>updateDisplay()</code> 関数の閉じ中括弧のすぐ上に置く必要があります。</p> + + <pre class="brush: js notranslate">request.send();</pre> + </li> + <li> + <p>今の時点でのこの例題にある問題の一つは、最初に読み込まれた時点ではなにも詩が表示されないことです。これを直すには、あなたのコードの一番下 (<code></script></code> 閉じタグのすぐ上) に以下の二行を追加し、デフォルトで1番の詩を読み込みませ、{{htmlelement("select")}} 要素に適切な値を指させます:</p> + + <pre class="brush: js notranslate">updateDisplay('Verse 1'); +verseChoose.value = 'Verse 1';</pre> + </li> +</ol> + +<h3 id="サーバからあなたの例題を送らせる">サーバからあなたの例題を送らせる</h3> + +<p>今時のブラウザ (Chrome も含まれます) は、ローカルファイルとして例題を実行しても XHR リクエストを行ないません。これはセキュリティの制限によるものです (Web のセキュリティにより詳しくは <a href="/docs/Learn/Server-side/First_steps/Website_security">Webサイトのセキュリティ</a>を読んで下さい)。</p> + +<p>これをどうにかするため、例題をローカルの Web サーバを使って実行しなければなりません。どうやるのかは、 <a href="/docs/Learn/Common_questions/set_up_a_local_testing_server">テスト用のローカルサーバを設定するにはどうすればいい?</a> を読んで下さい。</p> + +<h3 id="Fetch">Fetch</h3> + +<p>Fetch API は、基本的には XHR の今風の代替品です — 最近になってブラウザに組込まれたもので、非同期 HTTP リクエストを JavaScript で、開発者や他の Fetch の上に組まれた API から簡単に行なえるようにするためのものです。</p> + +<p>先の例を Fetch を使うように書き換えてみましょう!</p> + +<ol> + <li> + <p>さっき完成させた例題のディレクトリのコピーを作ります(前の例題を完成させていないなら、新しいディレクトリを作成して、そこに <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/xhr-basic.html">xhr-basic.html</a> と4つのテキストファイル — (<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse1.txt">verse1.txt</a>、<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse2.txt">verse2.txt</a>、<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse3.txt">verse3.txt</a> と <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse4.txt">verse4.txt</a>) のコピーを作って下さい。</p> + </li> + <li> + <p><code>updateDisplay()</code> 関数の中から、XHR のコードを探し出します:</p> + + <pre class="brush: js notranslate">let request = new XMLHttpRequest(); +request.open('GET', url); +request.responseType = 'text'; + +request.onload = function() { + poemDisplay.textContent = request.response; +}; + +request.send();</pre> + </li> + <li> + <p>XHR のコードを次のように置き換えます:</p> + + <pre class="brush: js notranslate">fetch(url).then(function(response) { + response.text().then(function(text) { + poemDisplay.textContent = text; + }); +});</pre> + </li> + <li> + <p>例題をブラウザに読み込むと(Web サーバから読んで下さい)、XHR 版と同様に動作するするはずです。今時のブラウザを使っていれば。</p> + </li> +</ol> + +<h4 id="Fetch_のコードでは何が起きている">Fetch のコードでは何が起きている?</h4> + +<p>まず最初に、{{domxref("WorkerOrWindowGlobalScope.fetch()","fetch()")}} メソッドが呼ばれ、取得したいリソースの URL が渡されています。これは XHR の {{domxref("XMLHttpRequest.open","request.open()")}} の今時な同等品で、さらに言えば <code>.send()</code> に相当するものは必要ありません。</p> + +<p>その後に、{{jsxref("Promise.then",".then()")}} メソッドが <code>fetch()</code> の後に連鎖されているのがわかるでしょう — このメソッドは {{jsxref("Promise","Promises")}} の一部で、非同期処理を行なうための今風な JavaScript に備わる機能です。<code>fetch()</code> はプロミスを返し、これはサーバから送られたレスポンスによって解決されます — <code>.then()</code> を使ってプロミスが解決された後にある種後始末のコードを走らせるようにし、そのコードとは内側で定義した関数にあたります。これは XHR 版の <code>onload</code> イベントハンドラに相当します。</p> + +<p>この関数には、<code>fetch()</code> のプロミスが解決された際に、自動的にサーバからのレスポンスが引数として渡されます。関数の中で、レスポンスをつかまえてその {{domxref("Body.text","text()")}} メソッド、これは基本的にレスポンスを生のテキストで返すもの、を走らせます。これは XHR 版の <code>request.responseType = 'text'</code> 部分と等価です。</p> + +<p><code>text()</code> もプロミスを返しているのがおわかりでしょう、ですのでそれに別の <code>.then()</code> を連鎖させ、その中で <code>text()</code> のプロミスが解決する生テキストを受けとるよう、関数を定義します。</p> + +<p>内側のプロミスの関数の中で、XHR 版でやったのとほとんど同じ事をやっています — {{htmlelement("pre")}} 要素のテキストコンテントにテキスト値を設定しています。</p> + +<h3 id="Aside_on_promises">Aside on promises</h3> + +<p>プロミスは初めて見るとちょっと混乱させられますが、今はひとまずそんなに心配しなくて大丈夫です。ちょっとすれば慣れます、とくに今風の JavaScript APIを学んでいけば — 新しい部分の大半がこのプロミスに強く依存しています。</p> + +<p>上の例のプロミスの構造を見直してみましょう、もうちょっと意味が通じてくるかもしれません:</p> + +<pre class="brush: js notranslate">fetch(url).then(function(response) { + response.text().then(function(text) { + poemDisplay.textContent = text; + }); +});</pre> + +<p>最初の行で言っているのは、「urlにあるリソースを取ってこい(fetch)」(<code>fetch(url)</code>)で、「それから(then)プロミスが解決したら指定した関数を実行しろ」(<code>.then(function() { ... })</code>)です。「解決」とは、「この先どこかの時点で、指定された処理の実行を終える」事を意味します。この場合だと指定された処理とは、指定のURLからリソースを取ってきて(HTTPリクエストを使って)、そのレスポンスを私たちがどうにかできるように返せ、です。</p> + +<p>実際のところ、<code>then()</code>に渡される関数は、すぐには実行されないコードの塊です — すぐにではなく、未来のどこかの時点でレスポンスが返って来た時に実行されます。頭に入れておいて下さい、プロミスは変数に保存する事もできて、変数に {{jsxref("Promise.then",".then()")}} を連鎖する事ができます。次のコードがやっているのも同じ事です:</p> + +<pre class="brush: js notranslate">let myFetch = fetch(url); + +myFetch.then(function(response) { + response.text().then(function(text) { + poemDisplay.textContent = text; + }); +});</pre> + +<p><code>fetch()</code> メソッドは HTTP レスポンスによって解決されるプロミスを返し、その後ろに連鎖された <code>.then()</code> の中にどのような関数を定義しても、それには引数としてレスポンスが自動で渡されます。引数にどんな名前を付けるのもご自由です — 下の例もちゃんと動きます:</p> + +<pre class="brush: js notranslate">fetch(url).then(function(dogBiscuits) { + dogBiscuits.text().then(function(text) { + poemDisplay.textContent = text; + }); +});</pre> + +<p>ですがパラメータにはその中身がわかる名前を付けた方がいいですよね!</p> + +<p>今度は関数だけに着目しましょう:</p> + +<pre class="brush: js notranslate">function(response) { + response.text().then(function(text) { + poemDisplay.textContent = text; + }); +}</pre> + +<p>レスポンスオブジェクトには {{domxref("Body.text","text()")}} メソッドがあって、これはレスポンスボディにある生データを受けて、プレインテキスト(これが私たちの必要とする形式です)、に変換します。このメソッドもプロミス(これは結果となるテキスト文字列で解決します)を返すので、ここでまた別の {{jsxref("Promise.then",".then()")}} を使い、この内部で、テキスト文字列を使って私たちがやりたい事を行うための別の関数を定義します。私たちがやるのは、ただ詩用の {{htmlelement("pre")}} 要素の <code><a href="/docs/Web/API/Node/textContent">textContent</a></code> プロパティをテキスト文字列と同じに設定だけなので、これはとても単純です。</p> + +<p>これも覚えておく価値があります、それぞれのブロックの結果を次のブロックに渡していくように、直接複数のプロミスブロック(<code>.then()</code>ブロック以外の種類もあります)を次から次へと連鎖する事ができます、あたかも鎖を下にたどっていくように。このおかげで、プロミスはとても強力なのです。</p> + +<p>次のブロックはもとの例題と同じ事をしますが、違うやり方で書かれています:</p> + +<pre class="brush: js notranslate">fetch(url).then(function(response) { + return response.text() +}).then(function(text) { + poemDisplay.textContent = text; +});</pre> + +<p>多くの開発者はこの書き方の方が好きです、なぜなら平らで、間違いなく長大なプロミス連鎖も読みやすいからです — それぞれのプロミスが、前のやつの内側に来る(これは扱いづらくなる場合があります)のではなく、前のやつから順々に続いています。違うのは <code><a href="/docs/Learn/JavaScript/Building_blocks/Return_values">return</a></code> 文を response.text() の前に書いて、それが出した結果を次の鎖に渡すようにしなければならないところだけです。</p> + +<h3 id="どっちの機構を使うべき">どっちの機構を使うべき?</h3> + +<p>これは本当に、あなたがどんなプロジェクトを進めているかによります。XHR は長いこと存在しているので、様々なブラウザで非常によくサポートされています。一方 Fetch とプロミスは Web プラットフォームに最近追加されたものなので、ブラウザ界では結構サポートされているんですが、IE はサポートしていません。</p> + +<p>古いブラウザをサポートする必要があるのならば、XHR の方が良いでしょう。ですがあなたがもっと先進的なプロジェクトで働いて、古いブラウザの事でさして悩まないなら、Fetch が良い選択になるでしょう。</p> + +<p>本当はどっちも学ぶべきです — Fetch は IE が消えていくにつれ(IE は、Microsoft の新しい Edge ブラウザのおかげで開発が終了しています)どんどん一般的になっていくでしょうが、もうしばらくは XHR が必要でしょう。</p> + +<h2 id="もっとややこしい例題">もっとややこしい例題</h2> + +<p>この記事のまとめとして、Fetch のより興味深い使い方を示す、ちょっとばかり難しい例題を見ていきましょう。例題用に缶詰屋というサイトを作成しました — これは缶詰だけを売る仮想のお店です。これの <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/">GitHubでのライブ実行</a> と <a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/fetching-data/can-store">ソースコード</a> が見られます。</p> + +<p><img alt="A fake ecommerce site showing search options in the left hand column, and product search results in the right hand column." src="https://mdn.mozillademos.org/files/14779/can-store.png" style="display: block; margin: 0 auto;"></p> + +<p>デフォルトではサイトには全ての商品が表示されますが、左側のカラムにあるフォームコントロールからカテゴリから、検索語から、あるいはその両方によってフィルタリングをかけられます。</p> + +<p>商品をカテゴリや検索語によってフィルタリングする処理をし、UIでデータが正しく表示されるように文字列を操作するためなどに、けっこうな量の複雑なコードがあります。この記事のなかでそれら全てについて解説しませんが、ソースコードのコメントに詳しいことがたくさん書いてあります(<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-script.js">can-script.js</a>を見て下さい)。</p> + +<p>ですが、Fetch のコードについては説明していきます。</p> + +<p>Fetch を使うブロックの最初は、JavaScript の初めの方にあります:</p> + +<pre class="brush: js notranslate">fetch('products.json').then(function(response) { + return response.json(); +}).then(function(json) { + let products = json; + initialize(products); +}).catch(function(err) { + console.log('Fetch problem: ' + err.message); +});</pre> + +<p><code>fetch()</code> 関数はプロミスを返します。これが成功裏に完了すると、一つ目の <code>.then()</code> ブロックの中にある関数は、ネットワークから返された <code>response</code> を受け取ります。</p> + +<p>この関数の中で、{{domxref("Body.text","text()")}} ではなくて {{domxref("Body.json","json()")}} を実行しています。プレインテキストではなく、構造化された JSON データとしてレスポンスを返してほしいからです。</p> + +<p>次に、別の <code>.then()</code> を最初の <code>.then()</code> の後に連鎖させています。これに、<code>response.json()</code> プロミスから返された <code>json</code> を含む成功時の関数を渡しています。この <code>json</code> を <code>products</code> 変数の値として代入してから、<code>initialize(products)</code> を実行します。すべての商品をユーザーインターフェイスに表示する処理が開始されます。</p> + +<p>エラーを処理するために、連鎖の最後に <code>.catch()</code> ブロックを連鎖させています。これは、何らかの理由でプロミスが失敗した場合に実行されます。その中には、引数として渡される関数、<code>error</code> オブジェクトが含まれています。この <code>error</code> オブジェクトを使用して、発生したエラーがどういうものかを伝えられます。ここでは単純な <code>console.log()</code> を使用して伝えています。</p> + +<p>ただし、完全な Web サイトでは、ユーザの画面にメッセージを表示し、状況を改善する選択肢を提供することで、このエラーをより適切に処理するでしょう。とは言え、ここでは単純な <code>console.log()</code> 意外は必要ありません。</p> + +<p>あなたは自分でも失敗した場合のテストができます:</p> + +<ol> + <li>例題のファイルのローカルコピーを作成して下さい(<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-store.zip?raw=true">缶詰屋の ZIPファイル</a>をダウンロードして展開して下さい)。</li> + <li>コードを Web サーバから読んで走らせるようにします(方法は前に {{anch("Serving your example from a server")}}で解説しました)。</li> + <li>fetch するファイルのパスを、'produc.json' のようなものに変更します(誤ったファイル名にして下さい)。</li> + <li>ここでインデックスファイルをブラウザに読み込んで( <code>localhost:8000</code> から)、あなたのブラウザの開発者コンソールを見ます。次の行のようなメッセージが表示されるはずです「Network request for produc.json failed with response 404: File not found」。</li> +</ol> + +<p>二つ目の Fetch ブロックは <code>fetchBlob()</code> 関数の中にあります:</p> + +<pre class="brush: js notranslate">fetch(url).then(function(response) { + return response.blob(); +}).then(function(blob) { + // Convert the blob to an object URL — this is basically a temporary internal URL + // that points to an object stored inside the browser + let objectURL = URL.createObjectURL(blob); + // invoke showProduct + showProduct(objectURL, product); +});</pre> + +<p>これも前のとおおよそ同じように動作しますが、{{domxref("Body.json","json()")}} ではなくて {{domxref("Body.blob","blob()")}} を使っているところが違います — 今回の場合は画像ファイルを返したいので、これ用に使うデータ形式は <a href="/ja/docs/Web/API/Blob">Blob</a> — これは "<u>B</u>inary <u>L</u>arge <u>Ob</u>ject" の略で、たいていは巨大なファイルのようなオブジェクト、画像や動画のようなものを示すのに使われます。</p> + +<p>blob を成功裏に受信したら、{{domxref("URL.createObjectURL()", "createObjectURL()")}}を使ってそこからオブジェクトURLを取り出します。これはそのブラウザの中でのみ有効なオブジェクトを示す一時的な URL を返します。あまり読み易いものではありませんが、缶詰屋アプリを開いて画像を Ctrlクリックもしくは右クリックして、メニューから「画像を表示」を選択する(これはあなたが使っているブラウザによって異なる場合があります)と見ることができます。オブジェクトURLはブラウザのアドレスバーに表示され、こんな感じになるでしょう:</p> + +<pre class="notranslate">blob:http://localhost:7800/9b75250e-5279-e249-884f-d03eb1fd84f4</pre> + +<h3 id="課題_XHR_版の缶詰屋">課題: XHR 版の缶詰屋</h3> + +<p>ちょっとした練習として、アプリの Fetch 版を XHR を使うように書き換えて下さい。<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-store.zip?raw=true">ZIPファイル </a>のコピーを作って、上手く JavaScript を書き換えてみて下さい。</p> + +<p>ちょっとしたヒントです:</p> + +<ul> + <li>{{domxref("XMLHttpRequest")}} のリファレンス記事が役に立つでしょう。</li> + <li>基本的には、初めの方の <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/xhr-basic.html">XHR-basic.html</a> の例で見たのと同じようなパターンを使う必要があります。</li> + <li>ただし、Fetch 版の缶詰屋でお見せしたのと同様なエラー処理を追加する必要があります: + <ul> + <li><code>load</code> イベントが発火した後は、プロミスの <code>then()</code> の中ではなく、<code>request.response</code> の中にレスポンスはあります。</li> + <li>XHR において、Fetch の <code>response.ok</code> に相当する一番良いやり方は、{{domxref("XMLHttpRequest.status","request.status")}} が 200 であるか、{{domxref("XMLHttpRequest.readyState","request.readyState")}} が 4 である事をチェックする事です。</li> + <li>ステータスとステータスメッセージを取得するためのプロパティは一緒ですが、これは <code>response</code> オブジェクトの中ではなく <code>request</code>(XHR)オブジェクトの中にあります。</li> + </ul> + </li> +</ul> + +<div class="note"> +<p><strong>注記</strong>: 上手くいかないときは、我々のGitHubにある完成版のコード (<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store-xhr/can-script.js">ソースコードはこちらから</a>、<a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store-xhr/">ライブ実行版</a>もどうぞ) と比べてみて下さい。</p> +</div> + +<h2 id="まとめ">まとめ</h2> + +<p>私たちのサーバからのデータ取得に関する記事は以上です。ここまでくれば、どう XHR と Fetch を使って進めていけばいいのか理解できたことでしょう。</p> + +<h2 id="あわせて参照">あわせて参照</h2> + +<p>この記事には様々なほんのさわりしか説明していない事項がたくさんあります。これらの事項についてもっと詳しくは、以下の記事を見て下さい:</p> + +<ul> + <li><a href="/ja/docs/Web/Guide/AJAX/Getting_Started">Ajax — 始めましょう</a></li> + <li><a href="/ja/docs/Web/API/Fetch_API/Using_Fetch">Fetch を使う</a></li> + <li><a href="/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a></li> + <li><a href="/ja/docs/Learn/JavaScript/Objects/JSON">JSON データの操作</a></li> + <li><a href="/ja/docs/Web/HTTP/Overview">HTTP の概要</a></li> + <li><a href="/ja/docs/Learn/Server-side">サーバサイド Web サイトプログラミング</a></li> +</ul> + +<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div> + +<h2 id="このモジュール">このモジュール</h2> + +<div> +<ul> + <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Web API の紹介</a></li> + <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">ドキュメントの操作</a></li> + <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">サーバからのデータ取得</a></li> + <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">サードパーティ API</a></li> + <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">グラフィックの描画</a></li> + <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">動画と音声の API</a></li> + <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">クライアント側ストレージ</a></li> +</ul> +</div> |
