続・jQuery の時代で取り残されてたので、今風の書き方を勉強した

JavaScript, jQuery

前回の続きです。
宣言通り、let, template, fetch の三本立てです。

局所変数を定義する let

注意) Safari では動きません

JavaScript では変数を定義するときに var を使います。
この場合、変数のスコープは関数単位になります。
そのため、以下のような場合には for のスコープの外でも i の値が参照できてしまいます。

例えば、以下ようなミスをしてしまうと、書いた人の意図をとは違う動きをしてしまいます。

let を使って変数宣言すると、ブロックスコープの変数を作ることが出来ます。
以下の例のように、ブロックの外で参照しようとしてもエラーになります。

広いスコープで変数を持たせることは、メリットよりもデメリットの方が多いです。
使えるのならば、let はどんどん使っていきたいです。

要素の雛形を定義する template タグ

注意)IE と Safari では動きません

HTML5 から便利な template というタグが追加されました。
JavaScript を使って DOM の要素を追加するとき、追加する要素の雛形を template タグで用意することが出来ます。

一覧の画面で「宛先表示」というボタンを押すと、画面にオーバーラップするような感じで宛先の一覧テーブルが表示されます。
これは ajax を使ってデータを取得して描画するので、HTML 要素の生成についても JavaScript で行う必要がありました。

書いている途中で絶対に間違っていると何度も思いましたが、
一応、これでもちゃんと動きます。
ソースを表示するとインデントとか完全に崩れてますが、
一応、これでもちゃんと動きます。

が、流石にこれを完成品としてしまうのは納得できなかったので、別の方法を探したところ、template タグについての記述を見つけました。

上の酷いスクリプトを、まず文字列ベタ書きだった HTML の部分を template タグで書き換えます。

上記の物を適当な場所(head か body の中ならどこでもいいそうです)に置きます。

次に、for 文の中身をこのテンプレートを用いた形に書き換えます。
ついでに、原始的な for 文を for-of の形に書き直しましょう。

コード量としては倍になってしまいましたが、動的要素と静的要素が分離できたので、コードの可用性としては上がったと思います。

見た感じで何となく分かると思うのですが、ちょっとだけ解説します。

template タグの要素は content という属性を持っており、中身の HTML 要素へは content を使ってアクセスします。
(innerHTML などを使ったアクセスはできません。これが普通のタグとの違いです)
この例では、template タグの中身にある td 要素の配列を取得しています。

そして、各要素に必要な情報を詰めます。

次に、テンプレートの中身を document に importNode 関数を使って書き出しています。
importNodeの 第二引数はtemplate タグ内の要素を全て追加するかどうかのフラグです。
第二引数を false にすると、templateタグ の直下にあるタグしか追加されません。

最後に、この要素を所定の場所に追加します。
(この例では appendChild ですが、順番などをもっと指定したい場合には insertBefore を使います)

contentimportNode にちょっと面食らいますが、実際に書いてみるとそれ以外には特に難しいことはないと思います。

Ajax 通信(等)のための fetch API

注意)IE と Safari では動きません

fetch とは?

ちょっと前までは、JavaScript の機能を使って Ajax を実装しようとすると XMLHttpRequest という API を使う必要がありました。
これが死ぬほど使いづらい API で、当時流行っていた「えいじゃっくす?」を試してみようと思った学生時代の私は絶望しました。
$.ajax で簡潔に書ける jQuery が流行ったことも納得です。

fetch API を使うと、
XMLHttpRequest よりも簡単にリクエストの送信やレスポンスの受信が書けるようになります。

fetch の現状

少し脱線しますが、WIZY では SuerAgentという、 Ajax 用のライブラリを使っています。
jQuery や SuperAgent は外部のライブラリを読み込む必要があるのですが、IE 含め最近のブラウザならば大体は動きます。
一方、fetch はまだまだ仕様策定中であり、実装しているブラウザも Google Chrome や FireFox, Edge だけです。
サービスに使える段階にはまだまだありません。今後に期待しましょう。

Promise オブジェクト

fetch を説明するにあたり、まず fetch が使用している Promise オブジェクト について説明をします。
Promise は JavaScript の悪評の一つである「コールバック地獄」を回避するためのオブジェクトです。

Ajax の様にどこかの API と通信を行って、その結果を基に処理を行うような場合、JavaScript では非同期の処理になります。
これまで、JavaScriptの非同期処理で関数を順番に実行するにはコールバック関数で書く必要がありました。
そのため、APIから値を取得 -> その値を元にさらにAPIから値を取得 -> さらに… とやっていくと、どんどんコールバック関数のネストが深くなります。これが俗に言う「コールバック地獄」です。

同じ処理を Promise オブジェクトを使った関数で書き直すとこうなります。
(関数の書き方はここでは割愛します。知りたい方は、最後にある参考のページを参照してください)
所謂メソッドチェインの書き方が出来るので、実行の順序がぱっと見でとても分かりやすくなります。

fetch もこの書き方で記述出来ます。

Fetch の使い方例

例えば、 /user/profile というエンドポイントに対して GET リクエストを行う場合、

でリクエストを投げて結果を受け取ります。
結果を JSON 形式にしてコンソールに出力するには以下のように書きます。

JSON 以外にも、blob (画像データなど) や text (HTML データなど) についても同様の書き方で書けます。

また、エンドポイントの後 Object 形式でオプションを記述することができます。

例えば、GET 以外のリクエストメソッドを使う場合もオプションに記述します。

補足

Ajax 通信で POST 等を行う場合には、成功/失敗を HTTP ステータスコードでチェックすると思います。
fetch は内部で例外が発生すると、 catch という場所に強制的に飛ばされるのですが、catch 内で API からのエラーメッセージを書く処理は、ありがちな割には、結構トリッキーな書き方が必要になります。
(jQuery もこの辺りの処理が書きやすかったとは言い難いですが…)

なお、fetch はデフォルトでは Cookie を送信しません。
認証が必要なAPIがあったときでも、jQuery や SuperAgentならば勝手にヘッダに認証情報を付けてリクエストを投げてくれます。
一方、fetchは認証情報を付けるには credentials オプションで明示的に書く必要があります。

  • same-origin: 同一のドメインであれば認証情報を送信する
  • include: 常に認証情報を送信する

他のライブラリに慣れているとハマる可能性があるので気をつけてください。
私はこれで20分ハマりました。

参考

JavaScript, jQuery