Quantcast
Channel: YoheiM.NET - 世の中をさらに便利に、面白く
Viewing all 364 articles
Browse latest View live

[JS]JavaScriptにおけるPromise/A+仕様を、チュートリアル形式で詳しく解説します

$
0
0
こんにちは、@yoheiMuneです。
JavaScriptの実装において、「Promise」を聞いたことがある方も多いのではないでしょうか。 今回は、JavaScriptにおけるPromise/A+を深く学ぶことができる記事を翻訳しました。その内容を紹介したいと思います。

画像

Special Thanks to https://flic.kr/p/b5bNqv



翻訳について

本記事は、Matt GreerJavaScript Promises ... In Wicked Detailの翻訳記事です。翻訳するにあたり、ご本人から許可を頂いています。 本記事では、JavaScriptにおけるPromiseの仕様がどっぷりと解説されていますので、お時間を割いて読んで頂く価値があると思います!
それでは、始まりますー!!



はじめに

私はJavaScriptを使った開発において、長らくPromiseを使ってきました。Promiseは、最初のうちはとっつきにくいものでした。今となっては、それをとても効果的に使えていますが、Promiseの詳細な部分となると、どのように動作しているのか十分には理解できていません。そんな私自身の問題を解決するために、この記事を書きました。(長いですが)最後まで読むことで、読者の方々もPromiseについて詳しく理解できると思います。

この記事では、Promiseの仕様を少しずつ実装していき、最終的にはPromise/A+の仕様をほぼ満たすように実装します。また、なぜPromiseが非同期プログラミングをサポートする必要があるのか、についても解説します。本記事は、ある程度Promiseを知っている方を想定しています。もしPromiseについて初めて触れるという方は、まずpromisejs.orgをご覧頂くと良いと思います。



目次

  1. なぜPromiseの詳細を扱うのか?
  2. もっともシンプルな例
  • Promiseは状態を持つ
  • Promiseにおけるメソッドチェーン
  • Promiseを否認する
  • Promiseの解決には、非同期が必要です
  • then/promiseの話を締めくくる前に
  • 最後に
  • 更なる学習のために



  • なぜPromiseの詳細を扱うのか?

    どうして、Promiseを詳しく理解する必要があるのでしょうか。それは、Promiseがどのように動作しているのかについて深く理解することで、それを活用する能力が向上するためです。また、何らかの問題が起きた時にも、より効果的にデバッグすることができます。この記事を書くきっかけとなったことは、私と同僚がPromiseの特殊な使い方に悩まされたことでした。その時は大変な思いをしましたが、そのおかげでPromiseについてより深く理解することができました。



    もっともシンプルな例

    まず初めに、最もシンプルなPromiseの実装をしてみましょう。以下の関数があるとします。
    doSomething(function(value) {
      console.log('Got a value:' value);
    });
    この関数を、次のように使いたいとします。
    doSomething().then(function(value) {
      console.log('Got a value:' value);
    });
    これを実現するために、doSomething()の実装を以下のような実装から
    function doSomething(callback) {
      var value = 42;
      callback(value);
    }
    次のような「Promise」を用いた実装に変更します。
    function doSomething() {
      return {
        then: function(callback) {
          var value = 42;
          callback(value);
        }
      };
    }
    Fiddle
    この実装はCalbackパターンの単なる置き換えであり、これだけではあまり意味のあるものではありません。しかし、とてもシンプルな実装であるものの、Promiseの根本的な考え方を既に捉えています。

    “”Promiseは、そのオブジェクト内に処理結果を保持する””

    これが、Promiseがすばらしいと感じる最も大きなポイントです。一度、Promise内に処理結果が保持されれば、その後はとても強力に物事を進めることができます。この点については後ほど説明します。



    Promiseクラスを定義する
    上記の単純なオブジェクトリテラルな実装では、すぐに上手くいかなくなります。ここでは、これからより詳細な説明を行うために、Promiseクラスを定義します。
    function Promise(fn) {
      var callback = null;
      this.then = function(cb) {
        callback = cb;
      };
    
      function resolve(value) {
        callback(value);
      }
    
      fn(resolve);
    }
    
    これを用いてdoSomething()をリファクタリングすると、次のようになります。
    function doSomething() {
      return new Promise(function(resolve) {
        var value = 42;
        resolve(value);
      });
    }
    さて、ここで問題があります。実行してみると分かりますが、resolve()then()よりも先に呼び出され、その結果、callbacknullとなってしまいます。この問題に対応するため、ここではsetTimeoutを使って(ハックとなりますが)対応しましょう。
    function Promise(fn) {
      var callback = null;
      this.then = function(cb) {
        callback = cb;
      };
    
      function resolve(value) {
        // callbackの呼び出しについて
        // イベントループを1つ待つことで、
        // then()が呼び出される機会を作ります
        setTimeout(function() {
          callback(value);
        }, 1);
      }
    
      fn(resolve);
    }
    
    fiddle
    このハックにより、とりあえずはコードが動くようになります。


    このコードは脆く、そして良くない
    このPromiseの実装は脆く、正常に動作させるために、非同期処理を利用する必要があります。この実装でエラーを発生させることは簡単で、then()を非同期に呼び出すせばよいのです。そうすれば、再びcallbacknullになってしまいます。なぜ、こんなにも脆いコードを紹介したのか、と疑問に思うかもしれませんね。それは、この実装はとても理解しやすいものだからです。上記のような簡単な実装であれば、Promiseで大切なthen()resolve()がコードの中ではっきりと分かると思います。then()resolve()は、Promiseにおいて重要な概念です。



    Promiseは状態を持つ

    先ほど紹介した脆いコードは、思いがけなくあることを明らかにしました。それは、Promiseは状態を持つ、ということです。私たちは、処理を開始する前にPromiseがどのような状態であるかを知る必要があり、またその状態がどのように変化するかを正確に把握する必要があります。それでは、コードの脆い部分を取り除きましょう。

    • Promiseは値が確定するまではpending状態で、値が決まるとresolved状態となります。
    • 一度解決された値は、その後は常にその値を保持し、もう一度解決を行うことはありません。

    (Promiseにはrejectedという状態もありますが、エラーハンドリングについては後ほど紹介します)

    setTimeoutのハックを取り除き、状態の変化を正確に把握できるように、実装を修正します。
    function Promise(fn) {
      var state = 'pending';
      var value;
      var deferred;
    
      function resolve(newValue) {
        value = newValue;
        state = 'resolved';
    
        if(deferred) {
          handle(deferred);
        }
      }
    
      function handle(onResolved) {
        if(state === 'pending') {
          deferred = onResolved;
          return;
        }
    
        onResolved(value);
      }
    
      this.then = function(onResolved) {
        handle(onResolved);
      };
    
      fn(resolve);
    }
    
    Fiddle
    少し複雑になりましたが、処理を呼び出す側はいつでも好きな時にthen()を呼ぶ事ができるようになりました。また呼び出される側は、任意のタイミングでresolve()を呼ぶことができるようになりました。同期でも非同期でもどちらにも完全に対応しています。

    これは、stateフラグを利用しているためです。then()resolve()も、新しいhandle()というメソッドに処理を委譲します。handle()では、状況に応じて2つの処理を使い分けます。

    • 呼び出される側がresolve()を実行する前に、呼び出し側がthen()を実行した場合は、値がまだ解決されていない状態です。この場合、statepending状態であり、呼び出し側が指定したcallackは、値の解決が行われるまで保持されます。その後、resolve()が実行されると、callbackは実行され、呼び出し側に値が戻ります。
    • 呼び出し側がthen()を呼び出す前に、呼び出される側がresolve()を実行した場合、値は確定した状態となります。この場合、then()が呼び出されればすぐに、値を返却します。

    setTimeoutの実装ははなくなってしまいましたが、実は、後ほど復活します。詳細はその時に説明します。

    Promiseを用いると、then()resolve()の呼び出し順序を考慮する必要なくなります。then()resolve()は、利用状況に応じていつでも呼び出す事ができます。この点は、Promiseが処理結果を保持するという機能の、とても大きなメリットの1つです。

    実装すべき仕様は他にもまだまだありますが、この時点で、私たちのPromiseはかなり仕様を実装しています。この実装を利用すれば、then()を何度呼び出したとしても、常に同じ値を受け取る事ができます。

    var promise = doSomething();
    
    promise.then(function(value) {
      console.log('Got a value:', value);
    });
    
    promise.then(function(value) {
      console.log('Got the same value again:', value);
    });
    

    この記事では、Promiseの仕様の全てを正確に実装している訳ではありません。例えば、もしresolve()が呼び出される前に複数回then()を呼び出した場合、最後に呼び出したthen()のみが有効となります。この問題を解決する1つの方法は、callbackを配列として保持することです。しかし、ここではその実装は行っていません。それは、Promiseの概念を紹介する上で、コードをできるだけシンプルにしたいためです。今までの実装でも、Promiseを説明するために十分に機能します。

    Promiseオブジェクトは、処理結果として立ち振る舞います。Promiseを、必要な時に好きなだけ、他の処理に渡したり、保持したり、利用したりすることができます。



    Promiseにおけるメソッドチェーン

    Promiseはオブジェクト内に値を保持しているため、メソッドチェーンをしたり、map処理をしたり、並列したり、直列したり、と様々な使い方をすることができます。次のコードは、良くあるPromiseの利用例です。
    getSomeData()
    .then(filterTheData)
    .then(processTheData)
    .then(displayTheData);
    getSomeData()は、直後にthen()が呼ばれていることからも分かるとおり、Promiseを返却しています。そして、1つ目のthen()の結果もPromiseであり、その返却値を使って次のthen()を呼び出しています(その次もしかりです)。then()でPromiseを返却することで、メソッドチェーンを行っているのです。

    then()は必ずPromiseを返却する

    ここで、私たちのPromiseクラスに、メソッドチェーンの概念を追加しましょう。
    function Promise(fn) {
      var state = 'pending';
      var value;
      var deferred = null;
    
      function resolve(newValue) {
        value = newValue;
        state = 'resolved';
    
        if(deferred) {
          handle(deferred);
        }
      }
    
      function handle(handler) {
        if(state === 'pending') {
          deferred = handler;
          return;
        }
    
        if(!handler.onResolved) {
          handler.resolve(value);
          return;
        }
    
        var ret = handler.onResolved(value);
        handler.resolve(ret);
      }
    
      this.then = function(onResolved) {
        return new Promise(function(resolve) {
          handle({
            onResolved: onResolved,
            resolve: resolve
          });
        });
      };
    
      fn(resolve);
    }
    
    Fiddle
    さて、コードが少し複雑になりました。ここで重要なポイントは、then()が新しいPromiseを返すということです。

    then()が常に新しいPromiseオブジェクトを返すため、一連の処理において、1つ以上の複数のPromiseが存在する場合があります。これは、一見オブジェクトの無駄遣いではないかと思うかもしれません。Callbackの手法では、このような問題は起こりません。これは、Promiseが批判を受ける主要なポイントです。しかし、JavaScriptを扱ういくつかのコミュニティでは、この点からPromiseを敬遠するようなことはやめて、正しく評価しようという取り組み始まっています。

    2番目のPromiseは、何の値を解決するのでしょうか。その値は、1番目のPromiseの返却値です。2番目のPromiseは、1番目の返却値を引数に受け取ります。この処理は、handle()内の後半部分に実装されており、引数のhandlerには、resolve()への参照とonResolvedへの参照を保持しています。そこにはresolve()のコピーが存在し、各Promiseは自分自身のresolve()のコピーや内部で実行するクロージャーを保持します。これが、1つ目のPromiseと2つ目のPromiseの架け橋となります。1つ目のPromiseを次のコードで解決しています。
    var ret = handler.onResolved(value);
    この例では、handler.onResolvedは次の通りです。
    function(value) {
      console.log("Got a value:", value);
    }
    違う見方をすれば、これが最初の呼び出しとしてthen()に渡される処理です。1番目のhandlerの返却値が、2番目のPromiseを解決するために利用されます。これで、メソッドチェーンの実装は完成です。
    doSomething().then(function(result) {
      console.log('first result', result);
      return 88;
    }).then(function(secondResult) {
      console.log('second result', secondResult);
    });
    
    // アウトプットは、
    // first result 42
    // second result 88
    
    
    doSomething().then(function(result) {
      console.log('first result', result);
      // 何もreturnしていない
    }).then(function(secondResult) {
      console.log('second result', secondResult);
    });
    
    // アウトプットは、
    // first result 42
    // second result undefined
    
    then()は常に新しいPromiseオブジェクトを返すため、好きなだけメソッドチェーンをつなげる事ができます。
    doSomething().then(function(result) {
      console.log('first result', result);
      return 88;
    }).then(function(secondResult) {
      console.log('second result', secondResult);
      return 99;
    }).then(function(thirdResult) {
      console.log('third result', thirdResult);
      return 200;
    }).then(function(fourthResult) {
      // これ以降も続けることができます
    });
    
    もし上記のコードで、最後に全ての処理結果を受け取りたい場合にはどうしたら良いでしょうか。メソッドチェーンを使って、必要に応じて独自に結果の受け渡しをすれば良いのです。
    doSomething().then(function(result) {
      var results = [result];
      results.push(88);
      return results;
    }).then(function(results) {
      results.push(99);
      return results;
    }).then(function(results) {
      console.log(results.join(', ');
    });
    
    // アウトプットは、
    // [42, 88, 99]
    
    Promiseは常に1つの値の解決のみを行います。もし2つ以上の値を一緒に渡したい場合、複数の値を扱える方法(配列、オブジェクト型、結合した文字列など)を利用する必要があります。

    Promiseをより良く使う方法として、Promiseライブラリのall()メソッドや、その他Promiseのメリットを引き出す何らかのUtilityメソッドを使うと良いでしょう。何を使うかについては、読者の皆さんの好みに合わせて選択してください。


    任意のCallback
    then()に指定するcallbackは、厳密に言えば必須ではありません。もしcallbackを省略した場合、Promiseは1つ前のPromiseと同じ内容を解決します。
    doSomething().then().then(function(result) {
      console.log('got a result', result);
    });
    
    // アウトプットは、
    // got a result 42
    
    既に実装済みのhandle()の内容を確認すると、callackがない場合には、Promiseを解決して、処理を終了するようになっていることが分かります。
    if(!handler.onResolved) {
      handler.resolve(value);
      return;
    }


    メソッドチェーンの内部でPromiseを返却する
    私たちのメソッドチェーンの実装には、少し繊細さが欠けるところがあります。私たちの実装では、受け取った値について何もチェックせずに、解決済みの値としてそのまま次の処理に渡しています。もし、受け取った値のの1つがPromiseオブジェクトだったらどうなるでしょうか。例えば、次のような場合です。
    doSomething().then(function(result) {
      // doSomethingElse returns a Promise
      return doSomethingElse(result)
    }).then(function(finalResult) {
      console.log("the final result is", finalResult);
    });
    このコードは、私たちが想定している通りに動きません。finalResultには、解決済みの値ではなく、Promiseオブジェクトが渡されます。この実装を意図通りに動作させるために、以下のように実装を修正してみましょう。
    doSomething().then(function(result) {
      // doSomethingElse returns a Promise
      return doSomethingElse(result)
    }).then(function(anotherPromise) {
      anotherPromise.then(function(finalResult) {
        console.log("the final result is", finalResult);
      });
    });
    かなり煩雑な実装となりました。この解決策として、Promiseの実装を変更して、解決済みの値がPromiseオブジェクトなのか否かを呼び出し側が意識せずに扱えるようにしましょう。これは簡単に実装することができます。resolve()に渡された値がPromiseオブジェクトだった場合に、特別な処理を行うだけです。
    function resolve(newValue) {
      if(newValue && typeof newValue.then === 'function') {
        newValue.then(resolve);
        return;
      }
      state = 'resolved';
      value = newValue;
    
      if(deferred) {
        handle(deferred);
      }
    }
    
    Fiddle
    Promiseを受け取る限り、resolve()を再帰的に呼び出し続けます。引数がPromiseでないものが見つかれば、その時点で処理を次に進めます。

    この実装は、無限ループとなる可能性があります。Promise/A+の仕様では、必須ではありませんが、無限ループに対処する実装が推奨されています。

    また、ここで紹介している実装は、仕様を完全には満たしていません。同様に、この記事で紹介している実装も、全ての仕様を満たしている訳ではありません。この点についてより詳しく知りたい場合には、仕様のPromise resolution procedureを読むことで、この記事では省いてしまった点も含めて、より正確に理解することができるでしょう。

    さて、newValueがPromiseオブジェクトかどうかの判定が、かなりいい加減だと思ったのではないでしょうか。then()メソッドがあるかどうかだけをチェックしています。このダックタイピングは意図的なものです。わざと曖昧に判定することで、Promiseの実装が少し異なるライブラリ同士を組み合わせた場合にも、お互いにPromiseだと解釈することができるようになります。複数のPromiseライブラリを混ぜ合わせることは、実際にはよくあることであり、それぞれのライブラリの実装が少し異なることも良くあることです。

    異なるPromiseの実装でも、Promiseの仕様に適切に従っていれば、お互いに解釈することができます。



    Promiseを否認する

    Promiseを扱っている時に何か問題が生じた場合は、理由とともにreject(=否認)する必要があります。呼び出し元は、否認された場合にどのようにそれを知れば良いのでしょうか。それは、then()の2つ目の引数にエラー時の処理を渡すことで、エラー通知を受けることができるようにします。
    doSomething().then(function(value) {
      console.log('Success!', value);
    }, function(error) {
      console.log('Uh oh', error);
    });
    以前に言及した通り、Promiseの状態は、pendingからresolvedrejectedのどちらかの状態に遷移します。両方の状態になることはありません。言い換えれば、then()の2つのコールバックのうち、どちらか1つのみが呼び出されるということです。

    Promiseはreject()を実行することでrejected状態にすることができ、reject()resolve()と同じように重要な機能です。それでは、doSomething()にエラーハンドリングを追加しましょう。
    function doSomething() {
      return new Promise(function(resolve, reject) {
        var result = somehowGetTheValue(); 
        if(result.error) {
          reject(result.error);
        } else {
          resolve(result.value);
        }
      });
    }
    Promiseクラスの中に否認機能を実装します。Promiseの仕様では、一度否認されれば、それ以降のすべてのPromiseも否認される必要があります。

    それでは、もう一度Promiseの実装の全体像を確認しましょう。今回は、否認機能が追加されています。
    function Promise(fn) {
      var state = 'pending';
      var value;
      var deferred = null;
    
      function resolve(newValue) {
        if(newValue && typeof newValue.then === 'function') {
          newValue.then(resolve, reject);
          return;
        }
        state = 'resolved';
        value = newValue;
    
        if(deferred) {
          handle(deferred);
        }
      }
    
      function reject(reason) {
        state = 'rejected';
        value = reason;
    
        if(deferred) {
          handle(deferred);
        }
      }
    
      function handle(handler) {
        if(state === 'pending') {
          deferred = handler;
          return;
        }
    
        var handlerCallback;
    
        if(state === 'resolved') {
          handlerCallback = handler.onRejected;
        } else {
          handlerCallback = handler.onResolved;
        }
    
        if(!handlerCallback) {
          if(state === 'resolved') {
            handler.resolve(value);
          } else {
            handler.reject(value);
          }
    
          return;
        }
    
        var ret = handlerCallback(value);
        handler.resolve(ret);
      }
    
      this.then = function(onResolved, onRejected) {
        return new Promise(function(resolve, reject) {
          handle({
            onResolved: onResolved,
            onRejected: onRejected,
            resolve: resolve,
            reject: reject
          });
        });
      };
    
      fn(resolve, reject);
    }
    
    Fiddle
    reject()を追加する以外にも、handle()でも否認を扱うようになりました。handle()内において、Promiseが否認されるか解決されるかは、stateの値によって決まります。このstateは次のPromiseオブジェクトにも渡され、そのPromiseでは受け取ったstateの値を元に、否認または解決を行い、また自分自身のstateの値に受け取ったstateの値を設定します。

    Promiseを使う際に、エラーコールバックを省略することができます。しかし、エラーコールバックを省略した場合には、何か問題が発生した際に、それを知る術がありません。少なくとも、チェーンしている最後のPromiseには、エラーコールバックを指定するべきです。後ほど、この点についてより詳しく説明します。


    予期せぬエラーも否認につなげる必要がある
    今までのところ、エラーハンドリングは、把握可能なエラーのみを対象にしていました。しかし、想定していないエラーが発生する可能性もあり、この場合にも正しく対処する必要があります。これは極めて重要なことであり、Promiseの実装では、想定外の例外を補足し、適切にrejectすることが求められます。

    これが意味するところは、resolve()try/cachブロックで囲われるべきだ、ということです。
    function resolve(newValue) {
      try {
        // ... as before
      } catch(e) {
        reject(e);
      }
    }
    また同じように重要な点として、呼び出し元が指定したコールバックが、予期せぬ例外を投げるかもしれないということです。これらのコールバックは、handle()内で呼び出されます。次のように対処します。
    function handle(deferred) {
      // ... as before
    
      var ret;
      try {
        ret = handlerCallback(value);
      } catch(e) {
        handler.reject(e);
        return;
      }
    
      handler.resolve(ret);
    }
    



    Promiseはエラーを握りつぶす可能性がある!

    Promiseに対する理解が不十分だと、エラーを完全に握りつぶしてしまう実装をしてしまう可能性があります。これは、Promiseの利用者がよくつまづくポイントです。

    次の例を考えてみましょう。
    function getSomeJson() {
      return new Promise(function(resolve, reject) {
        var badJson = "
    uh oh, this is not JSON at all!
    "; resolve(badJson); }); } getSomeJson().then(function(json) { var obj = JSON.parse(json); console.log(obj); }, function(error) { console.log('uh oh', error); });
    Fiddle
    さて、何が起こるでしょうか。then()の引数のコールバックは、正しい形式のJSONを受け取ることを期待しています。そのコールバックでは、受け取った値をチェックすることなくJSONのパースを試みるため、そこで例外が発生してしまいます。これを実装した人は、何かエラーが合った場合に備えて、then()の第2引数にエラーコールバックを指定しています。果たして、実装者の意図通りに、エラーコールバックが呼び出されるのでしょうか。

    答えはNoです。そのエラーコールバックは呼び出されません!上に掲載したfiddleの例を実行してみれば、何もアウトプットを得られないことが分かります。残念な結果ですね。

    なぜ、そのエラーコールバックは呼び出されないのでしょうか。これは、JSONのパースに失敗した例外はhandle()内でキャッチされますが、JSONのパース処理が呼び出される時には、対象のPromiseは既にresolved状態であるため、rejectは呼び出されないのです。その例外が起きた場合には、次のPromiseが否認されます。

    常に意識しておきたいこととして、then()のコールバックの中では、Promiseは既にresolved状態である、ということです。コールバックの結果が何であれ、Promiseに影響を与えることはありません。

    もし上記のエラーを捕捉したい場合には、エラーコールバックを、次のthen()で指定する必要があります。
    getSomeJson().then(function(json) {
      var obj = JSON.parse(json);
      console.log(obj);
    }).then(null, function(error) {
      console.log("an error occured: ", error);
    });
    これで、エラーを適切にログ出力できるようになります。

    私の経験上、この点がPromiseにおける最も大きな落とし穴です。次の章を読むことで、より良い解決策を理解することができます。



    done()を救済として使う
    (全てではありませんが)ほとんどのPromiseのライブラリには、done()メソッドが存在します。これはthen()にとても似ていますが、done()を使うことでthen()の落とし穴(前章を参照)を回避することができます。

    done()は、then()が記述できるところではいつでも呼び出すことができます。最も大きな違いは、done()はPromiseを返却しないことです。また、done()内で発生したいかなる例外もPromiseでは処理されません。言い換えれば、done()はPromiseのチェーンが全て終わった時に呼び出すということです。getSomeJson()の例でdone()を使うと、次のように書き直すことができます。
    getSomeJson().done(function(json) {
      // ここで発生した例外は、握りつぶされることはありません。
      var obj = JSON.parse(json);
      console.log(obj);
    });
    
    done()にもエラーコールバックを指定することができ、then()と同じく、done(callback, errback)といった具合に指定できます。そのエラーコールバック(errback)は、Promiseの処理が完全に終了してから呼び出されるので、Promiseを用いた一連の処理で発生したいかなるエラーも、通知を受けることができます。

    done()は(少なくとも今のところは)Promise/A+の仕様ではないため、Promiseのライブラリによっては、実装されていない場合もあります。



    Promiseの解決には、非同期が必要です

    この記事の最初では、setTimeoutを使うことで、ちょっとしたごまかし実装を行いました。そしてその後、そのハックを修正してsetTimeoutを使わなくなりました。しかし実際のところPromise/A+の仕様では、Promiseの解決は非同期で行われる、ということが想定されています。この仕様を満たすために、handle()の実装の大半をsetTimeoutの呼び出しで囲う必要があります。
    function handle(handler) {
      if(state === 'pending') {
        deferred = handler;
        return;
      }
      setTimeout(function() {
        // handle内の大半の処理
      }, 1);
    }
    
    Promise/A+で必要な実装は、以上で全てです。実際には、多くのPromiseのライブラリにおいて、非同期をサポートするためにsetTimeoutを使っていません。もしそのライブラリがNodeJS上で動作するのであれば、process.nextTickを使うでしょうし、もしブラウザ上で動作するのであれば、setImmediatesetImmediate shim(今のところIEのみ動作します)や、Kris Kowalのasapといったライブラリを使うかもしれません(Kris Kowalは、Qという人気のあるPromiseライブラリを作成しています)。



    なぜこのような非同期の要件が仕様にあるのか?
    非同期をサポートすることで、多くの呼び出し方に対応することができます。以下の奇妙な例を見てみましょう。
    var promise = doAnOperation();
    invokeSomething();
    promise.then(wrapItAllUp);
    invokeSomethingElse();
    この実装において、処理順はどのようになるでしょうか。関数名から推測すると、invokeSomething() -> invokeSomethingElse() -> wrapItAllUp()の順で呼び出されることが想定されているようです。しかし、想定通りの実行順となるかどうかは、Promiseの解決処理が、同期で行われるか、非同期で行われるかによって変わります。もしdoAnOperation()内で処理が非同期で行われるのであれば、想定通りの実行順となるでしょう。しかし、doAnOperation()が同期処理の場合は、処理の順序はinvokeSomething() -> wrapItAllUp() -> invokeSomethingElse()となり、想定とは異なる結果となってしまいます。

    この問題を回避するために、Promiseは常に非同期に解決を行う必要があります。例え、非同期である必要がないとしてもです。Promiseが非同期に解決を行うことで、Promiseの利用者は、Promiseが非同期処理に対応しているか否かを考慮する必要がなくなります。

    Promiseは常に、イベントループ(処理のメインスレッドにおけるループ)が1回以上経過した後に、解決を行うことが求められます。この仕様があるおかげで、コールバック手法が不要となります。



    then/promiseの話を締めくくる前に

    世の中には、Promiseの仕様を全て満たしたライブラリが数多く存在します。実装方法には様々な方法がありますが、その中でthenチームpromiseライブラリの実装は、比較的にこの記事と近い実装です。そのライブラリは、シンプルな実装で仕様を満たしており、また仕様以外のこと実装していません。もしその実装内容(@github)を見る機会があれば、その実装がこの記事ととても良く似ていることがわかるでしょう。thenチームのpromiseライブラリは、この記事を書くためのベースとなっており、私たちはここまでの実装で、ほぼ同様の実装をこの記事内で行いました。開発者であるNathan ZadoksとForbes LindsayのJavaScriptにおけるすばらしい活躍に、感謝したいと思います。また、Forbes Lindsayは、promisejs.orgのサイトを立ち上げた人でもあります。

    この記事で実装した内容と実際の実装には、いくつかの相違点が存在します。それは、Promise/A+にはここでは扱っていない更に詳しい仕様が定義されているからです。ぜひ、Promise/A+の仕様書(英語)を読んでみてください。仕様書は、比較的短く、とても読みやすい内容です。



    最後に

    最後まで、ご覧頂きましてありがとうございました。ここまで、Promiseの中核となる部分を扱ってきました。多くのPromiseを実装したライブラリでは、all(), race(), denodeify()など他にも様々な機能が提供されています。それらも含めたPromiseの可能性を知るには、API docs for Bluebirdを読むことをお勧めします。

    私は、Promiseがどのように動くのか、そして何を解決しようとしているのかを理解してからというもの、Promiseを本当に気に入っています。Promiseは、私のプロジェクトコードをとても簡潔にそして優雅にしてくれます。もっともっと話したいことがあります。この記事は序章でしかありません。

    もしこの記事を気に入って頂けたなら、私のTwitter(原作者のTwitterです)をフォローしてください。新しい記事をお知らせします。



    更なる学習のために

    より詳しくPromiseについて学ぶために役立つ情報を紹介します。

    • promisejs.org— 本文内でも何度か言及しましたが、Promiseに関するとても良いチュートリアルです。
    • Q’s Design Rationale— この記事と同じくPromiseを扱っています。本記事よりも詳細な内容が説明されています。
    • Some debate over whether done() is a good thing— Github上のIssueの1つですが、done()について深く議論されています。
    • Flattening Promise Chains— Thomas BurlesonによるPromiseの記事です。Promiseについて、とても詳しく説明しています。私の記事が「Promiseは何か」を説明しているとしたら、彼の記事は「Promiseのなぜ」を説明しています。



    間違いを見つけたら?

    もし記事内に間違いがありましたら、メールまたはGithubのIssueとして教えてください。



    翻訳を終えて最後に

    非常に長い記事でしたが、ここまでご覧頂いた方は、Promise/A+の内容を理解できたのではないでしょうか。 私自身、Promiseは聞いたことある、thenとかdoneとかいうメソッドを聞いたことあるというレベルでしたが、この記事を読むことで、Promise/A+の仕様の多くの部分を理解することが出来ました。 この記事を公開してくれて、さらに翻訳も快く許可してくれた、Matt Greerには本当に感謝です。

    今後もすごく良いと思った記事をまた翻訳したいと思いますので、ぜひ私のTwitterこのブログのRSSもどうぞ宜しくお願いします。

    最後までご覧頂きまして、本当にありがとうございました。




    [JS] XHR2の機能を学ぶ

    $
    0
    0
    こんにちは、@yoheiMuneです。
    今日は、とても便利なXMLHttpRequest version2の内容紹介を、ブログに書きたいと思います。

    画像

    Special Thanks to https://flic.kr/p/mCxBaM



    この記事の目的

    この記事は、XHR2の様々な機能を実装することを通して、XHR2について深く学ぶことを目的にしています。 自分はXHR2について便利そうだなぁという印象しか持っておらず、機能面や実装方法は無知でした。 そんな自分に対して、XHR2に対する確かな知識を持ちたいと思い、この記事を執筆することを決めました。



    目次




    XHR2とは

    XHR2とは、XMLHttpRequestのバージョン2です。W3Cではこちらのページで仕様策定が行われています。 XHR2では、XHR1で行えるサーバーとの非同期通信に加え、バイナリーデータを扱えたり、通信の進捗状況を把握できたりと、XHR1よりも機能が強化されています。
    それらの有益な機能を、実装可能な形で1つずつご紹介しきます。



    XHR2のサポート状況

    XHR2は全てのブラウザーで使えるわけではなく、現在はまだ仕様策定段階の機能です。Can I Useのサイトによれば、各ブラウザーのサポート状況は以下の通りです。
    画像

    引用:http://caniuse.com/#search=xhr

    IEとAndroid2.3でのサポート状況が少し微妙ですが、多くのPCブラウザやスマホブラウザで利用することができます。 XHR2サポート対象外のブラウザを少し考慮することで、十分に実務でも利用可能だと思います。



    XHR1でのバイナリーデータをダウンロードするハック

    XHR1でも画像などのバイナリーデータを、XHRを使って非同期に読み込むことができます。 しかしこの方法は、MimeTypeを上書きして、テキストデータからバイナリーデータを生成するという、かなりトリッキーなコードです。
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/labo/xhr2/image1.jpg', true);
    // MimeTypeを上書きする
    xhr.overrideMimeType('text/plain; charset=x-user-defined');
    xhr.onreadystatechange = function (e) {
        if (this.readyState === 4 && this.status === 200) {
            // レスポンスをテキストで受け取る
            var binStr = this.responseText;
            // 1文字ずつバイナリーとして扱う
            var base64 = 'data:image/jpeg,';
            for (var i = 0, len = binStr.length; i < len; i++) {
                var c = binStr.charCodeAt(i);
                // String.fromCharCode(c & 0xff)
                var aByte = c & 0xff;
                base64 += '%' + (aByte <= 0xf ? '0' : '') + aByte.toString(16);
            }
            // 作成したbase64を使って、imgタグを生成する
            var image = document.createElement('img');
            image.src = base64;
            document.body.appendChild(image);
        }
    }
    xhr.send();
    
    このトリッキーなコードは上手く行く時は良いですが、上手く行かないこともあったりと大変です。 XHR1ではこのようなトリッキーなコードを使いましたが、XHR2ではもっとエレガントにコードを書くことができます。 次節でXHR2を用いてコードを変更してみましょう。



    XHR2を用いて画像をダウンロードする

    それではXHR2を用いた画像のダウンロードを実装してみましょう。 XHR2ではレスポンスとして受け取るデータ型を色々と指定(仕様書参照)することができます。
    画像をダウンロードする場合には、データ型として「ArrayBuffer」または「Blob」を使います。それぞれの方法を試してみましょう。 まずは、ArrayBufferを使った実装です。
    // ArrayBufferデータ型で画像データをダウンロードする
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/labo/xhr2/image4.jpg', true);
    // ここで受け取るデータ型を指定します。
    xhr.responseType = 'arraybuffer';
    xhr.onload = function () {
        if (this.status === 200) {
            // this.responseにはArrayBufferであるバイト配列が格納されています。
            var uInt8Array = new Uint8Array(this.response);
            // バイト配列からbase64を生成します
            var base64 = 'data:image/jpeg,';
            for (var i = 0, len = uInt8Array.length; i < len; i++) {
                var aByte = uInt8Array[i];
                base64 += '%' + (aByte <= 0xf ? '0' : '') + aByte.toString(16);
            }
            // img要素を作成します
            var image = document.createElement('img');
            image.src = base64;
            document.body.appendChild(image);
        }
    };
    xhr.send();
    
    実装の途中にUint8Arrayが出てきましたが、これは8ビット符号なし整数値の配列です。 急に出てきてこれどのブラウザでも使えるのと気になったかもしれませんが、XHR2と共に使うことができるようです(参照:Can I Use)。
    以上が、ArrayBuffer形式でバイナリーデータを受け取れるという実装でした。

    次にBlobデータ型を用いた実装です。こちらもresponseTypeを指定することで利用することができます。
    // Blobデータ型で画像データをダウンロードする
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/labo/xhr2/image3.jpg', true);
    // responseTypeにblobを指定します
    xhr.responseType = 'blob';
    xhr.onload = function () {
        if (this.status === 200) {
            // responseにはblobデータが格納されています
            var blob = this.response;
            // URLという機能を用いて、blobデータのURL作成、URL有効化を行います。
            var URL = window.URL || window.webkitURL;
            var image = document.createElement('img');
            image.onload = function () {
                URL.revokeObjectURL(this.src);
            }
            image.src = URL.createObjectURL(blob);
            document.body.appendChild(image);
        }
    };
    xhr.send();
    
    こちらの実装でも新しいURLという機能が出てきました。 URLでは、Blobデータを参照するためのURLを生成したり、そのURLを有効化する機能が提供されています。 ブラウザサポート状況は、XHR2が利用可能なブラウザで同じく利用することができます(参照:Can I Use)。

    以上のように、XHR2を使うことで、画像などのバイナリーデータを特別なハックなしに非同期にダウンロードすることができます。
    それでは次に、アップロードについて話題を進めたいと思います。XHR2を用いることで、アップロードも自由自在に行うことができます。



    XHR2を用いたアップロード(テキスト、フォームデータ)

    先ほどはバイナリーデータを扱いましたが、この節ではまずはテキスト文字列やフォームデータをアップロードすることに取り組みます。 他多くのXHR2の記事を読んで自分が疑問に持った点として、アップロードしたデータをサーバー側でどうやって扱っているだろう、ということでした。 その点についても、PHPでの簡易を用いて紹介したいと思います。


    テキスト文字列をアップロードする
    最も基本となるテキストデータのアップロードを行いましょう。XHR2で実装すると、以下のようになります。
    // Client Code
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/labo/xhr2/server', true);
    xhr.responseType = 'text';
    xhr.onload = function () {
        if (this.status === 200) {
            console.log(this.responseText);
        }
    }
    // 文字列を送信します
    xhr.send('From SendTextBtn');
    
    xhr.sendメソッドの引数に、サーバーへ送りたい情報を指定することで、サーバーとの通信を行うことができます。
    上記のリクエストを受け取ったサーバーでは、以下のようにテキストデータを受け取ることができます。
    // Server Code (PHP)
    $contentType = $headers["Content-Type"];
    if (strpos($contentType, 'text/plain') !== false) {
        $requestBody = file_get_contents('php://input');
        echo "Thanks you! your post: '$requestBody'";
        return;
    }
    file_get_contents('php:input')を使って、送られてきたテキストデータを取得しています。 またコードをみると分かりますが、Content-Typetext/plainとなっていることが分かります。 xhr.sendでは、引数の内容に合わせて、自動的にContent-Typeが指定されます。


    フォームデータをアップロードする
    XHR2を用いるとフォームデータも簡単にアップロードすることができます。以下のように実装します。
    // Client Code
    
    // フォームデータを作成します
    var formData = new FormData();
    formData.append('username', 'yoheim');
    formData.append('location', 'japan');
    
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/labo/xhr2/server', true);
    xhr.onload = function () {
        if (this.status === 200) {
            console.log(this.responseText);
        }
    }
    // フォームデータを送ります
    xhr.send(formData);
    
    ここではFormDataクラスのインスタンスをxhr.sendの引数に渡すことでフォームデータを送っています。 このように、簡単にフォームデータを送信することができます。 またHTML上のformタグの情報も上記の要領で送信することができます。FormDataのインスタンス生成時に、formを渡します。
    // Client Code
    var formData = new FormData(document.forms[0]);

    このフォームを受け取るサーバーサイドの実装例は以下の通りです。
    // Server Code (PHP)
    $contentType = $headers["Content-Type"];
    if (strpos($contentType, "form-data") !== false) {
        $username = $_POST["username"];
        $location = $_POST["location"];
        $nickname = $_POST['nickname'];
        echo "Thank you for your post. formData[username='$username',location='$location',nickname='$nickname']";
        return;
    }
    ここではPOSTメソッドで送信しているため、$_POSTから値を取得しています。$_POSTから値を取得する実装は、良くある実装ですね。

    さてここまでで、XHR2を使ってテキストデータやフォームデータをサーバーへ送信する方法が分かりました。 次はバイナリーデータを非同期にサーバーへ送信する方法を扱いたいと思います。バイナリーデータとしてファイルを扱います。



    バイナリーデータをサーバーへ非同期に送信する

    続いては、XHR2を使ってバイナリーデータをサーバーへ送信することに取り組みましょう。ここではバイナリーデータとしてファイルオブジェクトを利用します。
    アップロードする方法には、Blobとしてアップロードすると、フォームデータに含めて送るの2種類があります。 それぞれの方法について見ていきましょう。


    Blobデータ形式でアップロードする
    まずはBlobデータとしてサーバーへ送信する方法です。以下のようなHTMLがあるとします。
    <input id="sendFileBtn" type="file" value="ファイルをBlobで非同期にアップロードする"/>
    このInput Fileタグでファイルが選択されたら、そのデータをサーバーへ送ってみましょう。
    // Client Code
    var btn = document.querySelector('#sendFileBtn');
    btn.onchange = function () {
    
        // 選択されたファイルを取得します.
        var file = this.files[0];
        console.log('filename: ', file.name);
        console.log('filesize: ', file.size);
        console.log('filetype: ', file.type);
    
        // xhrの準備をします.
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/labo/xhr2/server', true);
        xhr.onload = function () {
            if (this.status === 200) {
                console.log('file uploaded: ', this.responseText);
            }
        }
    
        // Fileを送信します.
        xhr.send(file);
    };
    
    xhr.sendには、Fileオブジェクトも指定することができます。 色々なデータ型を受け取ることができて、おぬしやりよるという感じです。 xhr.sendの引数にFileオブジェクトを指定した場合には、リクエストヘッダに付与されるContent-Typeはfile.typeを元に決定されます。
    以下のサーバーサイドの実装例は、JPEG画像がアップロードされた場合の実装です。
    // Server Code (PHP)
    
    // 今回はJPEGをアップロードしたので、Content-Typeはimage/jpegとなります.
    $contentType = $headers["Content-Type"];
    if (strpos($contentType, "image/jpeg") !== false) {
    
        // 保存先ファイルを準備します
        $fileName = 'tmpbox/aaa.jpg';
        if (!file_exists($fileName)) {
            touch($fileName);
        }
        // 「php://input」からアップロードされたバイナリーデータを取得します
        $blob = file_get_contents('php://input');
        $fp = fopen($fileName, "w");
        fwrite($fp, $blob);
        fclose($fp);
        echo "Thank you for your image.";
        return;
    }
    
    ただ、Blobデータのみをアップロードする場合には、ファイル名などの必要情報を持っていません。 それらの情報も必要な場合には、次に紹介するフォームデータに含めて送信する方法がよいでしょう。


    フォームデータに含めて送る
    ここではファイルデータをフォームに含めて、フォームデータをサーバーへ送信する方法です。 この方法を用いることで、ファイル自体のデータ以外にも、ファイル名などのメタ情報も一緒に渡すことができて便利です。
    var btn = document.querySelector('#sendFileBtn');
    btn.onchange = function () {
    
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/labo/xhr2/server', true);
        xhr.onload = function () {
            if (this.status === 200) {
                console.log('file uploaded: ', this.responseText);
            }
        }
    
        // フォームに含めてFileを送信します.
        var file = this.files[0];
        var formData = new FormData();
        formData.append('filename', file.name);
        formData.append('filetype', file.type);
        formData.append('content', file);
        xhr.send(formData);
    };
    
    Fileオブジェクトを、FormDataにappendすることで、バイナリーデータであるファイルを送ることができます。
    サーバーでの受け取りは以下のようになります。
    // Server Code (PHP)
    
    // フォームデータとして受け取ります.
    $contentType = $headers["Content-Type"];
    if (strpos($contentType, "form-data") !== false) {
    
        // 受け取ったファイル名から保存するパスを作成
        $filename = $_POST["filename"];
        $uploadfile = "tmpbox/$filename";
    
        // アップロードされたファイルの情報は$_FILESに格納されています
        // move_uploaded_fileを使ってアップロードされたファイルを取得します
        $result = move_uploaded_file($_FILES['content']['tmp_name'], $uploadfile);
        if ($result) {
            echo "success";
        } else {
        	# $_FILES[$key]['error']でエラー内容が分かります
            echo "file upload error.";
        }
    }
    
    このように、XHR2を使うことで、非同期にファイルなどのバイナリーデータのアップロードを行うことができます。 バイナリーデータを扱うことができる点が、XHR2がXHR1よりも優れている点の1つです。

    次の章では、XHR2に関する他の特筆すべき機能についても紹介したいと思います。



    その他XHR2の機能

    XHR2には、前述のバイナリーデータの扱いの他に、進捗状況の把握タイムアウト通信キャンセルといった機能が存在します。 それらの機能についても、少し触れたいと思います。


    進捗状況の把握(ダウンロード)
    XHR2では、どれほど処理が進んだのかを把握する機能が存在します。以前紹介した画像をダウンロードする実装を拡張して紹介します。
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/labo/xhr2/image3.jpg', true);
    xhr.responseType = 'blob';
    xhr.onload = function () {
        if (this.status === 200) {
            var blob = this.response;
            // ・・・Blobの処理
        }
    };
    // ダウンロードの進捗状況を取得します
    xhr.onprogress = function (e) {
        // e.loaded: ダウンロード済みのバイトサイズ
        // e.total:  トータルの倍とサイズ
        console.debug('[progress]', e.loaded ,e.total);
    }
    xhr.send();
    
    xhr.onprogressイベントに監視するための関数を指定することで、進捗状況を把握することが出来ます。 引数のeはProgressEvent(仕様書参照)で、そのプロパティから進捗情報を得ることができます。
    小さなサイズのデータであれば、onprogressは1回しか呼び出されませんが、大きなサイズ(2MBとか)になると、 何度か呼び出されて、ダウンロードの進捗状況を把握することができます。


    進捗状況の把握(アップロード)
    アップロードでもダウンロードと同様に、進捗状況を把握することができます。以前に紹介したフォームデータの送信の実装を拡張してみましょう。
    var btn = document.querySelector('#sendFileBtn');
    btn.onchange = function () {
    
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/labo/xhr2/server', true);
        xhr.onload = function () {
            if (this.status === 200) {
                console.log('file uploaded: ', this.responseText);
            }
        }
    
        // アップロードの進捗状況を把握
        xhr.upload.onprogress = function (e) {
            console.debug('[uploadProgress]', e.loaded ,e.total);
        };
    
        // 送信
        var file = this.files[0];
        var formData = new FormData();
        formData.append('filename', file.name);
        formData.append('filetype', file.type);
        formData.append('content', file);
        xhr.send(formData);
    };
    
    xhr.upload.onprogressに監視するための関数を指定することで、アップロードの進捗状況を把握することができます。 引数のeは、ダウンロードの時と同じくProgressEventです。
    進捗状況を表示できると、ユーザー体験の向上が行うことができ、サイトのクオリティが向上して良いですね!


    タイムアウトを設定する
    XHR2ではタイムアウトを簡単に設定することができ、またタイムアウトが発生した場合に通知を受け取ることができます。
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/labo/xhr2/server' ,true);
    xhr.onload = function () {/*成功時の処理*/}
    
    // タイムアウトを設定します
    xhr.timeout = 10; // 10ms
    
    // タイムアウト発生時のイベントを受け取ります
    xhr.ontimeout = function () {
         alert('timeout!!');
    }
    
    // 送信
    xhr.send('send message.');
    
    xhr.timeoutにタイムアウト時間(ミリ秒)を指定するだけでタイムアウトを設定することができます。とてもシンプルで強力な機能です。


    通信キャンセル
    XHR2では、通信のキャンセルを行うことができます。非同期通信をサーバーに送ってみたものの、ユーザー操作により中止したいということも時々存在します。 通信のキャンセルは以下のように行います。
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/labo/xhr2/server' ,true);
    xhr.onload = function () {/*通信成功時の処理*/}
    xhr.send('send message.');
    
    // 通信キャンセルが出来た場合に呼び出されるコールバック
    xhr.onabort = function () {
        console.debug('aboted!!');
    }
    
    // 通信中断の処理
    // ここでは10ms後に通信のキャンセルを行っています。
    setTimeout(function () {
        xhr.abort();
    }, 10);
    
    xhr.abort()を呼び出すことで、通信処理をキャンセルすることができ、 xhr.onabortに監視するための関数を登録することで、通信キャンセルがされた場合に通知を受けることができます。
    この機能で注意すべきことは、処理が既に終わっている通信はキャンセルすることができないという点です。 また、あくまでクライアント上での通信処理のキャンセルのため、サーバー上でなんらかのデータを更新するといった通信の場合には、通信中断には最新の注意を払う必要があります。



    参考資料

    この記事を書くために、以下の記事を参照しています。

    New Tricks in XMLHttpRequest2 - HTML5 Rocks
    XMLHttpRequest | W3C
    Progress Events | W3C
    Can I use... Support tables for HTML5, CSS3, etc
    PHP - 画像アップロード処理サンプル集 - Qiita
    Blob - Web API Interfaces | MDN
    ArrayBuffer - Web API Interfaces | MDN



    最後に

    今回は、XHR2について詳しく扱いました。 バイナリーデータが扱えること、進捗状況が分かること、などとても便利な機能が多くあることを学ぶことができ、良かったと思っています。 話の途中に出てきたBlobFileは、IndexedDBにも格納することができ、利用用途が多そうな機能だなぁと感じました。

    今後も特定の技術を掘り下げて扱う記事をいくつか書ければと思いますし、本職のフロントエンド記事もたくさん書いていきたいと思います。 ぜひ、RSSTwitterへの登録を、よろしくお願いします。 最後までご覧頂きましてありがとうございました。



    [フロントエンド] Chrome 35 Beta の変更点。Touch制御、新しいJavaScript機能、プレフィックスなしのShadowDOM

    $
    0
    0
    こんにちは、@yoheiMuneです。
    4/10のThe Chromium Blogによると、Chrome 35 betaがリリースされたようです。β版ということは、次のメジャーバージョンにも反映される内容です。
    かいつまんで、Chrome 35 betaでの追加/削除された機能をメモ的に残したいと思います。

    画像

    Special Thanks to https://flic.kr/p/mCxiCc



    Chrome beta版のインストール

    この記事で扱う機能は、Chrome 35 betaに搭載された機能です。そのため、安定板Chromeのバージョンが35になるまでは、β版で試す必要があります。 β版のインストール先を下記に記載しますので、そちらよりダウンロード&インストールしてみてください。

    https://www.google.com/intl/en/chrome/browser/beta.html



    TouchとZoomをより柔軟にコントロールする

    touch-action CSS propertyを使うことで、タップスクロール、ピンチによるズーム、ダブルタップによるズーム、といった挙動を選択的に制限することができるようになります。 これは、300ms click delay on mobile問題への対応や、精度の高いフリック、polyfills of Pointer Eventsなどに使えるようです。 またこれは、将来的にスクロールやズーム動作が、main threadを妨げないための布石ともなっているようです。



    JavaScript

    たくさんのECMAScript 6の機能がサポートされました。これらの機能を使うことで、より明快に、より強力に、よりメモリに優しいコーディングが可能となります。 注目されているPromiseはまだサポートされていませんが、近い将来に実装されるようです。

    またWeakMapWeakSetは、その名前の通り格納するデータに対して弱い参照を保持するデータ構造です。これを用いることで、Map内やSet内にしか存在せず、他から参照されていないようなデータについては、ガーベッジコレクション対象となり、メモリリークを防ぐことができます。Firefoxでは既に利用可能なようですね。

    Object.observe(obj, func)を使えば、JavaScriptのオブジェクトを監視することができ、監視対象のオブジェクトに変更があると、通知を受けることができます。これを使うことで、setTimeoutなどで対象オブジェクトを定期的に監視するようなコードを書く必要がなくなります。便利ですね。



    プレフィックスなしでShadow DOM

    Shadow DOMは、Web Componentsで定義する機能の1つです。Webコンポーネントを使うことで、以下のようなことを行うことができます。
    • HTMLやCSSにスコープの概念を持ち込むことができ、モジュール内にスコープを限定することができます。その結果、既存のモジュールとCSSがぶつかるといった問題を回避することができます。
    • Custom Elementを作ることができます。



    その他の新機能

    • CSS Font Loadingの機能を用いて、フォントを読み込むことができるようになりました(デモ)。必要な時にロードすることができ、ロードが完了した際に通知を受けることができます。
    • SVGのPaint Order Propertyを用いることで、fill、stroke、markerの描画順を指定することができるようになりました。
    • Web Audio APIがベンダープレフィックスなしで使えるようになりました。



    削除された機能

    Web健全性のため、またChromeの将来の進化のため、セキュリティ向上のために、削除された機能もあるようです。以下、列挙しておきます。
    HTMLVideoElement-specific prefixed fullscreen APITextTrackCue constructor<isindex>Legacy Web Notificationssupport for NPAPI on LinuxAttr.isId, ownerElement, prefix setterです。

    より詳しい情報は、mailing listで「Intent to Remove」や「Intent to Deprecate」のスレッドを参照してください。



    将来削除予定の機能

    Chrome 36では、window.showModalDialogが削除されるようです。IE6の頃に良く使ったなぁと思い出します。理由は、セキュリティ向上のため、実装が大変、ほとんど使われていないの3つのようです。より詳しい情報は、DEV.OPERAで解説されています。

    他にも、 Element.setAttributeNodeNSoverflowchanged EventHTMLSourceElement.mediaMediaError.MEDIA_ERR_ENCRYPTEDPrefixed media source extensionswebkitRequestAnimationFrame、 が削除予定のようです。



    最後に

    Chromeは仕事で使う中心的なツールの1つなので、しっかりと機能の増減についてはキャッチアップしていきたいとことです。 またChromium Blogの記事にはたくさんの関連リンクが掲載されており、それらを1つずつ見ていくのも本当に勉強になります。ぜひ見てみてください〜☆

    最後までご覧頂きましてありがとうございました。



    [JS] ECMAScript6をまるっと学ぶ。重要用語とか、仕様策定の進め方とか、新機能とか。

    $
    0
    0
    こんにちは、@yoheiMuneです。
    ECMAScript6について断片的には知っているけど、なんだかぼやっとしている。 そんな自分の状態を解消したいと思い、ECMAScript6を全体的に学べるブログを書きました。 ECMAScript6で使われている用語、仕様策定の進め方、そして新規機能を議題としています。 このブログを読んで頂いた方にも、ECMAScript6についての全体的な知識を提供できたらいいなと思ってます。それでは始まりですー。

    画像

    Special Thanks to https://flic.kr/p/baAtKT




    目次




    ECMAScript6とは

    ECMAScript6とは、現在広く使われているECMAScript5の次期バージョンです。 ECMAScript6(以下、ES6)では、letMapProxyといった各種ブログで取り上げられる新機能も含めた、仕様策定が行われています。

    ECMAScript6のコードネームは、Harmony(ハーモニー)です。 英語の記事を読むと「JavaScript Harmony」や「Harmony Draft Rev23」などの表現を見かけるかもしれませんが、それらはES6を意味しています。Harmonyという言葉はよく使われるので、覚えておくと英語の文献を読むときなどにかなり役立つと思います。

    Harmonyについては、harmony:harmony(英語)で概要が説明されており、何を目指しているのか、議論するテーマな何なのか、などを知ることができます。



    仕様策定の手順

    ES6の仕様策定手順は、正式にはECMAScript7から準拠するらしいTC39プロセスに則って、仕様策定が進んでいるようです。以下に、抜粋した内容を記載します。
    Strawman
    仕様検討するための議題を集めるような場所です。 ES6のstrawmanはstrawman:oldes6にありますが、ここで挙がった議題の多くは、Proposalには進んでいないようです。 また現在最新のStrawmanも存在します(strawman:strawman)。これはES6以降の仕様の検討材料となるようです。
    Proposal
    ProposalではStrawmanから上がってきた追加点を具体化するフェーズです。新機能や問題の輪郭を正確に捉える説明を作成したり、簡単なデモを作ってみたりします。またキーとなるアルゴリズムの検討や、分野横断的な検討も行われます。そのような活動に時間を費やすことで、次のDraftへ移ります。 ES6のProposalはharmony:proposalsにあり、ここに定義された機能のほとんどは、Draftとなっているようです。
    Draft(執筆時点では、このフェーズ)
    正式な仕様定義を行う形式を用いて、正確にシンタックスとセマンティクスの定義を行うフェーズです。このフェーズで、次期バージョンの最初の仕様書が作成されます。全ての主要な機能についてシンタックス、セマンティクス、APIの定義が行われますが、一部TODOや編集上の問題点も存在する状態となります。 ES6のDraftはharmony:specification_draftsにあり、最新の仕様書や、過去の履歴などを確認することができます。
    Candidate
    Draftで策定した仕様をもとに実際に実装が行われ、そのフィードバックから仕様を洗練させていきます。この段階で完全な仕様書が作成され、指定されたレビュアーやECMAScript編集者がそれを署名して認めます。
    Finished
    追加される仕様が、正式にECMAScriptに適用されることが示唆されます。test262に今回追加される機能のテストが記載され、それらのテストをパスする2つの準拠した実装が行われます。そして、直近の仕様書の更新に検討してきた内容が追加されます。
    Strawman、Proposal、Draftという単語も、ECMAScript6に関する記事では良く出てくるので、覚えておくと良いかもしれません。 執筆時の最新Draftは、2014/4/5のリビジョン23とのこと。これからが楽しみです。



    ECMAScript6のブラウザ対応状況

    @kangaxさんが作成しているECMAScript compatibility tableで、実装状況を確認する事が出来ます(以下、キャプチャ)。
    画像
    Firefoxの実装状況はいい感じですが、他はまだまだ赤い部分が多いですね。このサポート状況を個人でメンテナンスされているなんてすごい!大感謝です。



    ECMAScript6の新機能

    ECMAScript6の新機能は、harmony:proposalに記載された内容です。 本当はここに具体的な内容でブログを書こうと思ったのですが、調べれば調べるほど良い記事が見つかったので、それらの記事紹介をしたいと思います。


    lukehoban/es6features | Github(英語)
    個人的には、これが一番オススメです。数多くのES6新機能を用いた実装を、実際のコードと共に解説しています。2014年3月と最近できたレポジトリで、既に1300のStarが付いています。近いうちに日本語訳できればなぁと思ってます。


    気になる!夢がある!ECMAScript6についてガッツリ調べてみた。 (1) – ES6の基礎知識/関数のアロー記法/let/const/分割代入など | OpenWeb
    白石俊平さんが昨年(2013年1月)に書かれた記事。仕様に関するあれこれ、実装状況、ES6を試すツール/ライブラリ、そしてES6の新機能と、充実したラインナップです。 ただ、MapSetProxyといった内容は省かれているため、他の記事も合わせて読むと良さそうです。すばらしい纏めに感謝です。


    見えてきた「ECMAScript 6」。JavaScriptの生みの親が書く「Harmony of Dreams Come True」 - Publickey
    一昨年(2012年10月)と少し古い記事ですが、 JavaScript生みの親(Brendan Eich)がHarmony of Dreams Come Trueというブログを書かれ、その記事をPublickeyの新野さんが翻訳したものです。ガーベッジコレクションのことなど、英文も合わせて読むとES6に対する知識がより深まると思います。


    ES6 - Next Generation Javascript | Slideshare(英語)
    147ページという大集で、ES6の機能を説明しているスライドシェアです。さくさくと読めて良いです。


    YUI Theater — Dave Herman: “The Future of JavaScript” (48 min.) YUIのプレゼンテーションの動画。英語で48分で、JSのお勉強+英語のお勉強ができます。でもネイティブの英語は難しいー。


    ECMAScript6を実際に使ってみるには

    実際に使ってみるには、現時点では、ChromeかFirefoxを利用する必要があります。
    Chromeの場合には、chrome://flagsにアクセスし、「JavaScriptの試験運用機能を有効にする」を有効にする必要があります。
    また、白石さんのブログで紹介されていますが、google/traceur-compiler | GithubといったES6をES5へ変換するツールがあったり、paulmillr/es6-shim | GithubというES6で書いたコードをいい感じに動かしてくれるシムが存在します。



    その他情報ソース

    ECMAScript6について、他にもいくつか記事やまとめ内容がありましたので、コチラに記載します。気になったものがあれば、読んでみてください。

    Mozilla における ECMAScript 6 のサポート
    FirefoxのES6対応状況表ですが、ES6の機能を一覧で見れるという点もいいなと思います。


    ECMAScript6仕様書(HTML版)
    harmony:specification_draftsの内容を、HTMLで表現しているものです(必ずしも最新とは限らないですが、PDFよりも読みやすかったり)。


    ECMAScript | Wikipedia
    ECMAScript5以前の話とか歴史とかが書かれていて、読んでいて面白かったです。



    最後に

    以上、ECMAScript6の内容でした。調べてれば調べるほどたくさんの情報に出会いましたが、このような形でまとめることができてよかったです。
    Harmony、Strawman、Propsal、Draft、TC39、test262といったECMAScript関連の文言も学ぶことができ、ECMAScript6の今後の動向がすごく追いやすくなったのではないでしょうか。また、ES6の新機能についてもサンプルコードベースで学ぶことができ、知識を深めることができました。
    ECMAScript6は、たくさんの企業や個人によって仕様策定している状況です。JavaScriptの将来が楽しみです。

    最後までご覧頂きましてありがとうございました。



    [フロントエンド]複数アカウントでのテストには、Chromeのユーザー管理を使って、Cookieを切り替えると便利

    $
    0
    0
    こんにちは、@yoheiMuneです。

    皆さま、Webサービスのテストを複数アカウントで行う際に、どうしていますか?

    私は最近まで、Chromeのシークレットウインドウを駆使してアカウント切替をしていました。しかし最近Chromeのユーザー管理機能が便利だということを知りました。 今日は、Chromeのユーザー管理機能とは何か、どのように便利なのか、についてブログを書きたいと思います。

    画像

    Special Thanks to https://flic.kr/p/fedR9N




    目次




    Chromeのユーザー管理機能とは

    Chromeにはユーザー管理が行える機能があります。
    この機能を用いることで、Googleアカウントなどを簡単に切り替えることができ、複数のウインドウで異なるアカウントを使い分けることができるようになります。 そしてこの機能で嬉しい点は、ユーザーごとにCookieの内容も切り替わる、という点です。 この特徴を活用して、制作したサービスを複数アカウントで簡単にテストしよう、というわけです。

    まずは、Chromeのユーザー管理機能の使い方を説明します。



    Chromeのユーザー管理機能を利用する

    Chromeのユーザー管理機能を利用するには、Chromeの設定ページを開きます。
    画像 設定画面を開いたら、下の方に、「ユーザー」という項目があります。これがChromeのユーザー管理機能です。
    画像 「新しいユーザーを追加...」というボタンを押すと、以下のような画面が表示され、Chrome上にユーザーを追加することができます。
    画像 追加すると、以下のような新しいウインドウが立ち上がります。
    これで、ユーザー作成は完了です。
    画像 ユーザーを切り替えるには、右上に表示されるようになったユーザーアイコンをクリックして、表示されるユーザーリストから切り替えたいユーザーを選択します。
    画像 これで同一サイトで異なるCookieを持つユーザーを作成することができました。以下キャプチャのように、アカウントごとにウインドウを同時に立ち上げることができるので、アカウントを切り替えることは非常に簡単になりました。
    画像


    試しに使ってみる

    上記で作成したユーザーを試しに使ってみましょう。
    私は、私の英単語帳-Onlineというサービスを運営しています。 このサービスはログインしないとサービスが利用できません。オンライン上に英単語を登録して、PCでもスマホでもいつでもアクセスして、頑張って英単語を覚えようというサービスです。

    まずはデフォルトユーザー(「最初のユーザー」と表示されているやつ)で表示してみます。 既にログインしているため、自分のホームが表示されます。

    「最初のユーザー」でページを表示した結果

    画像

    ここで、ログイン前のページを確認したくなったとします。わざわざログアウトするのは面倒なので、先ほど作ったユーザー(「アカウント001」というやつ)で、 同じサイトを表示してみます。

    「アカウント001」でページを表示した結果

    画像 今度は、Welcomeページが表示されました。未ログインユーザーはWelcomeページへリダイレクトする仕組みなので、期待通りの動きです。

    このように、複数アカウントを切り替えて、簡単にテストすることができます。



    エクステンションもブックマークもまっさらに!

    直前の画面キャプチャを見て頂くと分かりますが、新規に作成したユーザーは、Chromeエクステンションが全く無い状態です。 サイトのテストによっては、Chromeエクステンションが悪さをしている場合もあるので、エクステンションを無効化してテストしたい場合にも、Chromeのユーザー管理機能は役立ちます。

    またブックマークもなくなるので、ページキャプチャとか撮る時に、恥ずかしいブックマークを晒さずにすみますw。



    最後に

    以上、Chromeのユーザー管理を用いた、複数アカウントでのテスト方法の説明でした。 お仕事でもこの機能にはとてもお世話になっています。 強いキャラ、弱いキャラ、お金持ちのキャラ、貧乏なキャラ、など色々なアカウントを使い分けてテストを行っています。 この機能が、どなたかの役立てば幸いです。

    最後までご覧頂きましてありがとうございました!



    [フロントエンド] Chrome36βが出た。変更点など。element.animate、HTML Imports、Object.observe、他。

    $
    0
    0
    こんにちは、@yoheiMuneです。
    5/22にThe Chromium Blog(英語)でChrome36β版のリリースが発表されました。ちょっと遅れましたが、36での機能の変更点について、ブログに書いておきたいと思います。

    画像

    Special Thanks to https://flic.kr/p/nKFp2v




    Chromeベータ版の入手、新機能の動作設定

    Chromeのβ版は、以下から入手することが可能です。
    https://www.google.com/intl/en/chrome/browser/beta.html

    また、追加された機能によっては、chrome://flagsから、「JavaScriptの試験運用機能を有効にする」を有効にする必要があります。



    element.animate()

    W3CのWeb Animation 1.0の仕様に合わせた、JavaScriptでアニメーションを行う機能が追加されたようです。 詳細はhtml5rocksの記事(英語)で解説されていますが、以下のような記述ができるようです。
    var element = document.querySelector('#someElm');
    element.animate([
      {cssProperty: value0},
      {cssProperty: value1},
      {cssProperty: value2},
      //...
    ], {
        duration: timeInMs,
        iterations: iterationCount,
        delay: delayValue
    });
    実際に、雪の降るアニメーションがサンプルとして実装されています(ポリフィル版はこちら)。 自分が使ってみた感じだと、イーズの指定ができないとか、開始と終了の少なくとも2つを指定する必要があったりと、少し気になる点もありました。



    HTML Imports

    Web Componentsの仕様の一つで、Chrome31で初めて実装されたImportの挙動が、最新仕様に対応したようです。詳細は、html5rocksの記事(日本語)で解説されていますが、以下のように使うことができます。
    <head><link rel="import" href="/path/to/imports/stuff.html"></head>
    インポートした内容は、以下のようにJavaScriptから利用できます。
    var content = document.querySelector('link[rel="import"]').import;
    html5rocksの記事(日本語)を読むと、この機能が非常に重要であることが分かりました。



    Object.observeのサポート

    Canaryで利用可能であったObject.observeが、Chrome36βから利用可能になったようです。 JavaScriptオブジェクトを監視して、変更があったら、その内容を受け取る仕組みです。データバインディングが必要なフレームワークなどで特に便利とのこと。 以下のように使うことができます。
    // 監視対象のオブジェクト
    var obj = {};
    
    // 監視する
    Object.observe(obj, function (changes) {
        changes.forEach(function (change) {
            console.log(change.type, change.name, change.oldValue);
        }); 
    });
    
    // 追加を検知する
    obj.prop1 = 'aaa'; // => add prop1 undefined
    
    // 更新を検知する
    obj.prop1 = 'bbb'; // => update prop1 aaa 
    
    // 削除を検知する
    delete obj.prop1;  // => delete prop1 bbb 



    その他

    • タップスクロール時にtouchcancelイベントを発火しないようになったようです。詳細はhtml5rocksを参照(英語)。プルリフレッシュなどの機能を無茶せず作れるようになるとのこと。

    • scrollTopoffsetHeightなどのCSSは、ブラウザがズーム表示されている時に、long値ではなく、float値を返すようになったようです。詳しくはChromiumのForum(英語)を参照。

    • WOFF2.0というWebFontのフォーマットをサポートしたようです。WOFF1.0に比べて圧縮率が良く、平均30%程度サイズを削減できるようです。詳しくはChromiumのForumを参照。

    • ディベロッパーツールで、タッチイベントを正確にエミュレートできるようになったようです。これでAndroid Chromeなどのテストもちゃんとできるよーと。詳しくはRick Byersさんのコメント(英語)を参照。

    • CSS Transformがベンダープレフィックスなしに利用可能になったようです。詳しくはChromiumのForum(英語)を参照。



    最後に

    Chromeの記事は毎回読むたびに、技術的に新しい発見があって、読んでいて楽しいです。ぜひ原文もご覧下さい。たくさんの興味深い関連リンクがあります!
    最後までご覧頂きましてありがとうございました!



    [フロントエンド]Androidのリモートデバッグも手軽にできるJSInspectorってのが素敵

    $
    0
    0
    こんにちは、@yoheiMuneです。
    Echo JSに何回か出てきていたので気になっていたJSInspector。 使ってみるとすんごくシンプルで簡単にリモートデバッグができました。今日はその紹介をしたいと思います。

    画像


    機能説明と使い方

    http://jsinspector.com/で紹介されている通り、 DOMの内容、Consoleログの内容、エラーの内容を簡単にリモートデバッグできるツールです。 使い方は簡単で、上記のサイトで掲載されている以下のようなコードをデバッグしたいページに仕込みます。
    <script src="http://jsinspector.com/inspector?9763-ACEAD"></script>
    そしてそのサイトをデバッグしたい端末(AndroidでもiPhoneでもPCでも)で開きます。その後、上記サイトにある「Open Inspector」ボタンを押してリモートデバッグを開始します。
    「Open Inspector」で開いたページでは、デバッグ対象のブラウザで表現されているDOMやConoleログやエラーを確認することができ、またスクロールなども同期されます。

    もちろんChromeのディベロッパーツールやSafariのWebインスペクタに比べれば機能は劣りますが、デバッグ用コードを仕込んで表示するだけで簡単にデバッグできるというのはすごく良いなぁーと思いました。



    動作の仕組み

    JSInspectorの仕組みは、switer/jsinspector@GithubでMITライセンスで公開されています。中身を確認するとどうもnode.jsのExpressでサーバーを立てて、Socket.IOで情報のやり取りをするという仕組みのようです。執筆時点では半月前くらいに出来た比較的若いレポジトリのようですね。 これぐらいのソース量でさくっとリモート出来るなんて素敵です。 ローカルに自前でサーバーを立てて利用することも可能なようなので、用途があれば使ってみたいところです。



    最後に

    簡単ではありますが、さくっとお手軽にリモートデバッグができるJSInspectorの紹介でした。 余談ですが最近は冒頭でも紹介したEcho JSを時々見て、面白そうな情報がないか探しています。 時々こんな感じの面白いものが見つかるので、ご興味ある方はぜひ見てみてください。また良きものがありましたら紹介させていただきます☆

    最後までご覧頂きましてありがとうございました!




    [JavaScript] Gruntの後発のフロントエンドビルドツール、gulpに入門

    $
    0
    0
    こんにちは、@yoheiMuneです。
    gulpというフロントエンドのビルドツールが気になってました。 それについて試したり調べたりしてだいぶ理解できたので、今日はその内容をブログに書きたいと思います。

    画像


    目次




    gulpとは

    gulpとは、JavaScriptのMinifyやCSSプリプロセッサのコンパイルなどを行うことができるフロントエンドのビルドツールです。 フロントエンドのビルドツールには有名なGrunt.jsがありますが、その後発のビルドツールのようです。


    Grunt.jsと何が違うのか
    実現できることはぶっちゃけ同じで、その実現方法や思想が異なるのだと思います。 gulpのサイト(英語)からいくつか主張を拝借してみました。

    gulp's use of streams and code-over-configuration makes for a simpler and more intuitive build.

    (gulpはnode.jsのstreamという機能を用いており、code-over-configuration(設定よりもコード)を重視します。そのためよりシンプルにより直感的にビルドを行うことができます。)

    gulpのGrunt.jsと大きく異なる点は、code-over-configurationという所です。 後ほど掲載するサンプルコードを見ると分かりますが、ビルドの処理をJavaScriptで記述します。 もちろんGrunt.jsもビルド処理をJavaScriptで記述します。しかしGruntではJavaScriptオブジェクトを定義する一方で、gulpはJavaScriptの実装そのものを書くというイメージです。


    また、gulpのサイトでは以下4つの特徴が強調されています。
    1. Easy to use
    コードでビルドタスクを実装するため、シンプルにタスクを定義することができます。
    2. Efficient
    node.jsのstream機能を用いているため、処理の中間ファイルなどを作ることなく、高速にビルドを行うことができます。
    3. High Quality
    gulpのプラグインは厳しいガイドラインに従って作成されなければいけません。それに違反すればブラックリストに登録される可能性があります。そのため、プラグインの品質は高く保たれるようです。
    4. Easy to Learn
    gulpのAPIの数は最小に保たれています。そのため覚えることはかなり少なく、簡単に使い始めることができます。
    プラグインの質がコントロールされている点は、Gruntと大きく異なる点です。個人的には少し怖いとも感じましたw。
    以上をまとめてみると、Grunt.jsとの違いは以下となるでしょうか。

    gulpとGruntの違い

    1. gulpはコードベースでタスクを定義する
    2. gulpはプラグインの品質がコントロールされている
    3. gulpはGruntに比べてコード量が少なくタスクを定義できる
    4. gulpはJavaScriptの実装ができる人じゃないと使えなさそう
    続いては、gulpを実際に使って見たいと思います。



    gulpの導入

    gulpの導入は以下のように行います。
    まずはターミナルでコマンドとして使うためのグローバルインストールを行います。
    $ [sudo] npm install -g gulp
    続いてビルドを行うプロジェクトで使うためにローカルにもインストールします。
    $ npm intall --save-dev gulp
    これでgulpのモジュールが作成できました。



    gulpをとりあえず動かしてみる

    gulpのタスクは、gulpfile.jsという名前のファイルに定義します。例えば「foo」という特に何もしないタスクを作ってみましょう。
    // gulpfile.js
    var gulp = require('gulp');
    
    // タスク定義
    gulp.task('foo', function () {
        console.log('foo task is called.');
    });
    
    これで「foo」というタスクを定義することができました。 このタスクを実行するには、以下のようにターミナルでコマンドを実行します。
    $ gulp foo
    [20:24:02] Using gulpfile ~/tmp/gulpfile.js
    [20:24:02] Starting 'foo'...
    foo task is called.
    [20:24:02] Finished 'foo' after 113 μs
    
    タスクの実行方法はGruntと似てますね。



    gulpでちゃんと意味あるタスクを動かしてみる

    上記のタスクだと使い物にならないので、ここではファイルの削除、ファイルのコピー、coffeeスクリプトのビルド、JSのミニファイ、画像の最適化を行うタスクを作ってみましょう。 まずは必要なnpmモジュールをインストールします。
    $ npm install --save-dev gulp-coffee
    $ npm install --save-dev gulp-concat
    $ npm install --save-dev gulp-uglify
    $ npm install --save-dev gulp-imagemin
    $ npm install --save-dev rimraf  # nodeで「rm -rf」を行うライブラリ
    
    続いて、gulpfile.jsを編集します。
    // gulpfile.js
    
    // 必要モジュールをロード
    var gulp = require('gulp');
    var coffee = require('gulp-coffee');
    var concat = require('gulp-concat');
    var uglify = require('gulp-uglify');
    var imagemin = require('gulp-imagemin');
    var rimraf = require('rimraf');
    
    // タスク:無意味...
    gulp.task('foo', function () {
        console.log('foo task is called.');
    });
    
    // タスク:ビルド先を削除
    gulp.task('clean', function(cb){
      rimraf('build/', cb);
    });
    
    // タスク:Coffeeのコンパイル、JSのミニファイ
    // ['clean']を指定することで、このタスク実行前に行いたいタスクを指定できます.
    gulp.task('scripts', ['clean'], function() {
      return gulp.src(['client/js/**/*.coffee', '!client/external/**/*.coffee'])
        .pipe(coffee())
        .pipe(uglify())
        .pipe(concat('all.min.js'))
        .pipe(gulp.dest('build/js'));
    });
    
    // タスク:画像の最適化
    gulp.task('images', ['clean'], function() {
     return gulp.src('client/img/**/*')
        .pipe(imagemin({optimizationLevel: 5}))
        .pipe(gulp.dest('build/img'));
    });
    
    // タスク:ファイルのコピー
    // コピーだけなら特にプラグインは不要.
    gulp.task('copy-html', function () {
        return gulp.src('*.html')
            .pipe(gulp.dest('build/'));
    });
    
    // デフォルトタスクを指定
    gulp.task('default', ['scripts', 'images', 'copy-html']);
    
    見て分かる通り、Gruntとはかなり記述内容が異なります。 同じ内容をGruntfileで定義しようとするとコード量はほぼ倍になるでしょう。また、gulpのAPIは、gulp.srcgulp.pipegulp.destの3つだけでAPIの数が最小だという点も分かります。
    個人的には少し癖があるかなーと感じますが、でも学んだ次の日には自分も特に参照することなくコードを書けたので、かなり学習コストは少ないと思います。

    上記を実行するには以下のようにターミナルで行います。
    $ gulp
    これでビルドができます。



    gulpでwatchをする

    gulpではwatchをすることも可能です。上記のgulpfile.jsに以下のタスクを追加します。
    gulp.task('watch', function() {
      gulp.watch(['client/js/**/*.coffee', '!client/external/**/*.coffee'], ['scripts']);
      gulp.watch('client/img/**/*', ['images']);
    });
    そして以下のようにターミナルで実行します。
    $ gulp watch
    これで指定したファイルが監視されるようになります。 また、gulp-livereloadというモジュールもあるようなので、watch後にlivereloadを行うこともできそうですね。



    プラグインを探す

    プラグインは、以下のサイトで公開されていますので、そちらから探すことができます。

    http://gulpjs.com/plugins/

    でもだいたい目星がついているならnpm searchで探すか、Google先生に聞いた方が早いかもですかね。



    (追記)Gruntから移行する際のメリット・デメリット

    記事を公開したら、「Gruntから移行するメリットは・・・」という話を受けましたので、考えてみました。 執筆時点の感想では「Grunt使っているならわざわざ移行しなくても良いかなー」というのがザックリとした感想です。
    Gruntからgulpへ移行する場合のメリットデメリットは以下となるでしょうか。

    メリット

    1. ファイルの行数が半分くらいになる。500行なら250行とか
    2. gulpの方が早いらしい(未検証)
    3. 最新ツール使ってるぞと、自慢できるw

    デメリット

    1. 日本語でのノウハウは少ないから、何かにはまったら自分で道を切り開く必要がある
    2. 行数が少ないって言ったって、たくさんタスクを定義したら長くなる
    3. JavaScriptを書ける人向け
    4. 何かカスタマイズしようとすると、node.jsのpipe処理を理解しておく必要がある

    個人的には「新しいもの好きで使い初めてみる。 気に入ってそのプロダクトの成長へコントリビュートする」というのが、楽しい過ごし方かなと思います。 感想ベースなので間違っていたらごめんなさい。この点、他の方にも意見を伺ってみたいですー。



    参考情報

    gulp紹介記事や、gulpが利用しているnodeのpipeについて詳しく書かれた記事を紹介します。 より深い理解にご利用下さい。

    gulpjs.comの本家ページ(英語)
    node.jsのstreamについて(英語)
    打倒Grunt!Node.js用の新たなビルドシステムgulpことはじめ(by白石さん)
    Gruntfile.js が長すぎてつらい人は gulp を使ってみよう | Qiita



    最後に

    gulpを触ってみた感想としては、すごく気軽にコード量少なくビルドコードが書ける点がいいなと思いました。 実際に仕事でもちょっと使ってみました(他メンバーはgulpを知らない状態なので、正規のビルドはもちろんGruntを使いますが)。
    今後も動向をwatchしつつ、少しずつ自分でも使ってみようと思いました。

    最後までご覧頂きましてありがとうございました。
    今後もこんな感じでフロントエンドの技術紹介をしていくつもりですので、良かったらRSSTwitterもよろしくお願いします☆




    [フロントエンド] 現在のブラウザシェアをサクッと把握したいならStatCounterが便利

    $
    0
    0
    こんにちは、@yoheiMuneです。
    今日はブラウザシェアを簡単に調べられる方法をご紹介したいと思います。

    画像

    Special Thanks to https://flic.kr/p/9zbbkX




    目次




    StatCounterとは

    StatCounterとはサイトのアクセス解析を行うことができるツールで、 いうなればGoogle Analyticsと同じようなものです。
    一般的な使い方としては、アカウントを登録して、発行されたアクセス解析用のプログラムを自分のサイトに埋め込んで、StatCounter上でUUとかPVとかを閲覧するという使い方です。 しかしStatCounterのすごいところは、それらの情報を集計して世界や国別のブラウザシェアなども確認できる機能を用意しているところです。

    具体的な使い方を見ていきましょう。



    StatCounterの使い方

    ここでは簡単にStatCounterの使い方を説明します。

    ※執筆時点での使い方のため、本記事をご覧頂いている時には変わっているかもしれません。

     

    まずは表示してみる
    まずは表示してみましょう。http://gs.statcounter.com/にアクセスします。 アクセスすると以下のように、全世界でのブラウザシェアを確認することができます。
    画像

    日本のブラウザシェアを確認する
    きっと本ブログをご覧の方であれば、日本でのブラウザシェアを確認したいはず! 国別のブラウザシェアを表示するには、以下図のRegionでJapanを選びます。
    画像

    表の表示形式を変更する
    折れ線グラフで時系列でのブラウザシェアの推移も大切な情報ですが、ある時点でのブラウザシェアも知りたい場合がありますよね。 その場合には、ページ右側にあるグラフの種類を変更することで目的の情報にアクセスすることができます。以下ではBarに変更してみました。
    画像

    モバイルのシェアも表示する
    さていままでの表ではデスクトップブラウザしか表示されていません。 モバイルのシェアを確認するには画面左下のStatをクリックして、表示したいブラウザにチェックを入れます。
    画像
    例えばPlatformでMobileのチェックを追加した場合は、以下のスクショのような表示に成ります。
    画像

    その他にも
    上記で紹介した以外にも「Screen Resolution(画面解像度別)」「Browser Version(ブラウザバージョン別)」「Operating System(OS別)」などがあり、色々と便利そうです。


    最後に

    今回はStatCounterの紹介でした。このような統計情報を一般公開してくれているとはありがたいですね。 この情報を使って、プレゼン資料を作成したり、サービスのサポート対象を決めたり、デザインの基本サイズを決めたりできそうです。

    最後までご覧頂きましてありがとうございました。



    [HTML5] Web Speech APIに入門

    $
    0
    0
    こんにちは、@yoheiMuneです。
    7月になってやっとブログを書く余裕が出てきた(´∀`∩)〜〜。 今日は、Chromeでも最近使えるようになったWeb Speech APIについて入門記事を書きたいと思います。

    画像

    Special Thanks to https://flic.kr/p/7fADmS




    目次




    Web Speech APIとは

    Web Speech APIはW3Cコミュニティグループによって策定されている仕様で、Web Speech API Specificationに仕様が掲載されています。この仕様書はリンク先にも記載の通り「It is not a W3C Standard nor is it on the W3C Standards Track(このドキュメントはW3C標準でもなければW3Cの標準トラックに則ったものでもない)」とW3C標準ではありません。しかしWebkitやBlinkエンジンを積んだいくつかのブラウザで既に利用可能となっています。

    Web Speech APIには大きく2つの機能があり、音声認識テキストスピーチです。 音声認識とはその名の通りユーザーがしゃべった内容を認識して文字にしてくれる機能です。 テキストスピーチはMacのsayコマンドのようなもので、指定した文字列をPCが発音してくれます。

    本機能の利用用途としては、例えば以下のようなものが考えられます。
    • 声によるWeb検索(AndroidやiPhone Siriのような機能)
    • 声によるマシンへの命令
    • Inputフィールドへの声による入力
    • ゲーム
    • など
    HTML5 Japan Cup 2014では本機能を用いたアプリケーションもあり、画面内のキャラクターがしゃべったり、ユーザーのタップに合わせて数値を発音するために使われています。

    Web Speech APIのサポート状況は、Can I Useによると以下の通りです。

    (執筆時点でのサポート状況です。最新状況はリンク先をご確認ください。)

    画像Web Speech API | Can I Use

    なんとiPhoneでも部分的に使えるというのが驚きです(筆者が試した時点では英語のテキストスピーチができました)。
    以降の章では具体的な使い方を紹介します。



    音声認識を使ってみる

    まずは一番簡単な例で、音声認識を行う実装例を紹介します。以下の例では、ボタンを押して音声認識を始めて、もう一度ボタンを押して音声認識を終了します。 認識された音声は枠の中に表示されます。
    画像http://yoheim.net/labo/html5/web-speech-api.html

    実際には「音声認識を始める」ボタンを押すと、Chrome上でマイクの利用許可が表示されます。 ここで「許可」を押すことでJavaScriptからマイクを利用することができるようになります。
    画像 実装内容は以下の通りです。 JavaScriptを通してこんな感じで簡単に音声認識が行えるなんて、素敵ですね。
    // 音声認識機能
    var recognition;
    
    // 音声認識中か否かのフラグ
    var nowRecognition = false;
    
    // 音声認識を開始するメソッド
    function start () {
        // 音声認識のインスタンスを作成します
        recognition = new webkitSpeechRecognition();
        // 利用言語を選択します(Chromeでは日本語も使えます)
        recognition.lang = document.querySelector('#select1').value; // en-US or ja-JP
        // 音声認識が終了したら結果を取り出すためのコールバック
        recognition.onresult = function (e) {
            if (e.results.length > 0) {
                var value = e.results[0][0].transcript;
                document.querySelector('#area1').textContent = value;
            }
        };
        // 音声認識開始
        recognition.start();
        nowRecognition = true;
    };
    
    // 音声認識を停止するメソッド
    function stop () {
        recognition.stop();
        nowRecognition = false;
    }
    
    // ボタンアクションを定義
    document.querySelector('#btn1').onclick = function () {
        
        // unsupported.
        if (!'webkitSpeechRecognition' in window) {
            alert('Web Speech API には未対応です.');
            return;
        }
        
        if (nowRecognition) {
            // 音声認識終了
            stop();
            this.value = '音声認識を始める';
            this.className = '';
        } else {
            // 音声認識開始
            start();
            this.value = '音声認識を止める';
            this.className = 'select';
        }
    }
    
    ポイントとしては「webkitSpeechRecognition」を使って音声認識の開始や終了、そして結果の取り出しを行う点です。 上記デモコード全体はhttp://jsdo.it/y.munesada/mPucに置きました。



    音声認識を使ってみる(継続的な認識を行う)

    続いて上記の機能を少し発展させて、音声認識を継続的に行う方法を紹介します。Googleもデモを用意していますが、ユーザーがしゃべっている間にもドンドンと音声認識の結果を取得することが出来ます。
    画像http://yoheim.net/labo/html5/web-speech-api.html

    具体的な実装内容は以下の通りです。
    // 音声認識機能
    var recognition;
    var nowRecognition = false;
    
    // 確定した結果を表示する場所
    var $finalSpan = document.querySelector('#final_span');
    
    // 音声認識中の不確かな情報を表示する場所
    var $interimSpan = document.querySelector('#interim_span');
    
    // 音声認識開始のメソッド
    function start () {
        recognition = new webkitSpeechRecognition();
        recognition.lang = document.querySelector('#select2').value;
        // 以下2点がポイント!!
        // 継続的に処理を行い、不確かな情報も取得可能とする.
        recognition.continuous = true;
        recognition.interimResults = true;
        // 音声結果を取得するコールバック
        recognition.onresult = function (e) {
            var finalText = '';
            var interimText = '';
            for (var i = 0; i < e.results.length; i++) {
                // isFinalがtrueの場合は確定した内容
                // 仕様書では「final」という変数名だが、Chromeでは「isFinal」のようです.
                if (e.results[i].isFinal) {
                    finalText += e.results[i][0].transcript;
                } else {
                    interimText += e.results[i][0].transcript;
                }
            }
            $interimSpan.textContent = interimText;
            $finalSpan.textContent = finalText;
        };
        recognition.start();
        nowRecognition = true;
    };
    
    // 音声認識を止めるメソッド
    function stop () {
        recognition.stop();
        nowRecognition = false;
    }
    
    // ボタンアクションの定義
    document.querySelector('#btn2').onclick = function () {
        
        // unsupported.
        if (!'webkitSpeechRecognition' in window) {
            alert('Web Speech API には未対応です.');
            return;
        }
        
        if (nowRecognition) {
            stop();
            this.value = '音声認識を継続的に行う';
            this.className = '';
        } else {
            start();
            this.value = '音声認識を止める';
            this.className = 'select';
        }
    }
    
    1つ前のサンプルと違う点は、recognition.continuous = truerecognition.interimResults = trueを設定して認識中の内容も取得可能とする点と、音声取得結果についてe.results[i].isFinalの判定を用いて、それが確定した情報なのか否かを判定している点です。
    こちらのコードもhttp://jsdo.it/y.munesada/iUgpに置きました。



    テキストスピーチ

    最後にテキストスピーチを扱います。今までの例とは異なり、ブラウザがしゃべります。
    画像http://yoheim.net/labo/html5/web-speech-api.html

    実装は以下のように簡単に行うことができます。
    document.querySelector('#btn3').onclick = function () {
    
        // unsupported.
        if (!'SpeechSynthesisUtterance' in window) {
            alert('Web Speech API には未対応です.');
            return;
        }
    
        // 話すための機能をインスタンス化して、色々と値を設定します.
        var msg = new SpeechSynthesisUtterance();
        msg.volume = 1;
        msg.rate = 1;
        msg.pitch = 2;
        msg.text = document.querySelector('#text1').value; // しゃべる内容
        msg.lang = document.querySelector('#selectVoice').value; // en-US or ja-UP
    
        // 終了した時の処理
        msg.onend = function (event) {
            console.log('speech end. time=' + event.elapsedTime + 's');
        }
    
        // テキストスピーチ開始
        speechSynthesis.speak(msg);
    };
    
    SpeechSynthesisUtteranceの詳細は、5.2 The SpeechSynthesis Interface | 仕様書で確認することができます。 Chromeだと日本語も発音してくれるのでとてもいい感じです! 具体的なコードは、http://jsdo.it/y.munesada/8qn5に置きました。



    最後に

    私が初めてWeb Speech APIについて知ったのは、5jcupに応募された作品を見た時でした。まだまだ知らない機能がいっぱいあるなーと思い、フロントエンドとして活動していますがまだまだ勉強不足と痛感した次第でした。
    今後のブログでもフロントエンド最前線(またはそれに近い)情報をお送りできたらと思っています。ぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。

    最後までご覧頂きましてありがとうございました!



    [FENews] 2014/7/4〜2014/07/10のストックしておきたいフロントエンド関連の海外記事

    $
    0
    0
    こんにちは、@yoheiMuneです。
    フロントエンドの最新動向を追いかけるため、日々色々なサイトから情報収集をしています。 この一週間で「あっこれいいな」と思った記事が風化しないように、ブログにメモを取っておきたいと思います。

    画像

    Special Thanks to http://flic.kr/p/kpTpJ6




    目次




    チュートリアル:HTML AudioのキャプチャをストリーミングでNode.JSに送信する(ブラウザエクステンションは使わない)

    [原題]Tutorial: HTML Audio Capture streaming to Node.js (no browser extensions)

    http://blog.groupbuddies.com/posts/39-tutorial-html-audio-capture-streaming-to-node-js-no-browser-extensions画像 この記事は、getUserMedia()を使ってマイクロフォンにアクセスし、そこで取得した情報をNode.JSのサーバーにどのように送信するのか、そしてサーバーは受け取った情報をどのようにファイルへ永続化するのかを示したチュートリアルです。 記事内のRound Oneではまず録音しきってからそれをサーバーに送る方式が書かれています。Round Twoではストリーミングでサーバーにアップする方法が記載されています。
    ブラウザ上のJSとNode.JSでこのような実装が出来るのかと、私は驚きでした。



    CSSでザ・シプソンズ

    [原題]The Simpsons in CSS

    http://pattle.github.io/simpsons-in-css/画像 少し前に日本でも、ドラえもんなどのアニメキャラをCSSで表現することが流行っていましたが、それの海外版です。 ザ・シプソンズのキャラクター達を画像なしにCSSのみで表現しています。 実装の中身を見るとかなり頑張っているなーという印象です。小さな黄色い破片のdivで線を消していたりとか。
    このような作品はCSSのアニメーション実装する時に参照するととても役立つので、ここに掲載しておこうと思いました。



    HTTP APIデザインガイド

    [原題] HTTP API Design Guide

    https://github.com/interagent/http-api-design/blob/master/README.md画像
    HTTP+JSONのAPIをどのように設計するかに関するガイドライン。HerokuのAPIをもとに作成されたようです。「適切なステータスコードを使う」「可能ならば全てのリソースをユーザーに返す」「Etagによるキャッシュ」「URL設計」など色々と参考になるものが多いです。



    HTML5 Canvasでパーティクルを実装する

    [原題] Creating Particles in HTML5 Canvas

    http://flippinawesome.org/2014/06/25/creating-particles-in-html5-canvas/画像
    初心者向けにHTML5 Canvasを使ってパーティクルを実装する方法を、ステップバイステップで説明しています。 canvasタグの配置から、オブジェクトの配置、オブジェクトの移動、ランダム性の導入、重力の導入、跳ね返りなど、詳しく学ぶことができます。



    JSONを用いてSassとJavaScriptでデータを共有する

    [原題] Sharing Data Between Sass and JavaScript with JSON

    http://viget.com/extend/sharing-data-between-sass-and-javascript-with-json画像
    このタイトルを見た時は、「SassでJSON参照できるのか!?」とびっくりしました。内容としては、Sassを拡張するモジュールを追加してSassからJSONを読み込めるようにするということでした。色味やサイズなどの定義がSassとJavaScriptで共有するという考え方良いですね♪。



    (function (window, document, undefined) {})(window, document);が意味することとは!?

    [原題] What (function (window, document, undefined) {})(window, document); really means

    http://toddmotto.com/what-function-window-document-undefined-iife-really-means/画像
    様々なJavaSriptライブラリのコードを読んでいて、確かにこの書き方には疑問を感じていました。 なんでundefinedをわざわざ引数に指定しているんだろうと。 これを読むことでなるほどとスッキリ理解することが出来ました。また記事内ではwindowdocumentを引数に設定している意味も解説しています。 JavaScriptは記法に独特なところが多いですが、この記事を読んで「アハ」体験をすることができました。



    その他にも

    今後自分で何か作る時に参考になるだろうなーという記事を記載したいと思います☆

    HTTPステータスコード定義
    http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
    W3Cで定義しているHTTPステータスコードの定義です。各ステータスコード(200,201,401,etc)がどのようなものかが理解できます。


    最後に

    最近はEchoJSHacker Newsを見て、海外から技術的な情報を仕入れる取り組みをしています。 日々たくさんの情報をインプットしていて溜まってきて、理解を深める意味でもアウトプットしようと思いました。 HTML5 Experts.jpでゆうやさんがされている感じがいいなーと思い、それをお手本に海外記事の紹介を今回から初めてみました。 ゆうやさんの記事に比べてかなり実装系の記事が多いですが、これは個人的な好みだと思います。不定期にはなりますが(出来れば1週間に1回くらいは出したい)、海外記事の発信をこのブログでも始めます!!!!!!!(と宣言してみる・・・)。

    今後もフロントエンドやその周辺情報を発信していきますので、ぜひともRSSTwitterをフォローをお願いします☆
    最後までご覧頂きましてありがとうございました。



    [フロントエンド] ブラウザレンダリングの仕組みを理解して、ブラウザに優しいJavaScriptを書こう

    $
    0
    0
    こんにちは、@yoheiMuneです。
    ブラウザのレンダリングの仕組みはHTML5 RocksHow browsers workで詳しく解説されてきました。しかしそれらはとても詳細で、読破して理解するのは大変です。
    今回のブログでは手軽にレンダリングの概要を理解できるように心がけました。またより詳しく学べるようなリンクも記載しました。
    そしてブラウザのレンダリングの仕組みを理解した上で、どのようなJavaScriptを書くべきかについても記載しました。
    画像


    目次




    ブラウザのレンダリングの仕組み

    この章では、HTMLとCSSが読み込まれてから画面に表示されるまでの間に、ブラウザがどのような処理を行っているかを説明します。 ファイル読み込みから表示までの一連の流れは以下図の通りです。
    画像
    • [1]
      読み込んだHTMLを解析してDOMツリーを生成します。解析方法の詳細は解析-概要 | HTML5Rocksをご参照ください。
    • [2]
      読み込んだCSSも解析してCSSの構造体を生成します。この構造体の呼び名はいくつかありますが、ここではCSSOM(CSS Object Model)と呼ぶこととします。CSSの解析の詳細はCSSの解析 | HTML5Rocksをご参照ください。
    • [3]
      DOMツリーとCSSOMから画面表示に必要なレンダーツリー(Render Tree)を構築します。レンダーツリーにはDOMの構造と装飾の両方の情報が含まれます。ここでレンダーツリーとDOMツリーの違いは何かと思うかもしれません。それは、DOMツリーには全てのDOMが格納されている一方で、レンダーツリーには表示する要素のみが格納される(headタグやdisplay:noneの要素は含まれない)ということです。この処理の詳細はレンダーツリーの構築 | HTML5 Rocksをご参照ください。
    • [4]
      レイアウト(またはリフロー)ではレンダーツリーが持つ各DOM要素の位置を決定します。「レイアウト」という呼び方はWebkit系で「リフロー」という呼び方はGekko系から来る呼び方です。この処理の詳細はレイアウト | HTML5Rocksをご参照ください。
    • [5]
      ペイントでは画面への描画処理を行います。この処理の結果ようやく画面に表示されます。この処理の詳細は描画 | HTML5Rocksをご参照ください。

    以上がレンダリングの仕組みの概要です。 各項目においてそれぞれ細かな処理内容はありますが、ざっくりとした概要は上記の通りです。

    画面の初期表示では上記の一連の処理が行われますが、JavaScriptによるDOM操作でもレンダリング処理の一部が実行されます。 JavaScriptからどのような処理を行うと、どのようなレンダリング処理が行われるのでしょうか。 次の章では具体的な例を用いて、その点を説明します。



    どのような時にレンダリング処理が発生するのか

    この章では、初期表示以外にどのような場面でレンダリング処理が発生するのかについて説明します。 この章を通して、どのような時にどんなレンダリング処理が発生するのかの勘所を掴んで頂けたら幸いです。

    JavaScriptで要素のスタイルを変更する
    JavaScriptから要素のスタイルを変更すると、変更した内容に応じてレイアウトやペイントが発生します。 以下の例では、bodyの各スタイルを変更した場合にどのようなレンダリングイベントが発生するかを示しています。
    // 余白を変更する
    document.body.style.padding = '30px'; // Layout と Paint が発生
    
    // ボーダーを変更する
    document.body.style.border = '10px solid red'; // Layout と Paint が発生
    
    // フォント色を変更する
    document.body.style.color = 'blue'; // paint のみ発生
    
    // 背景色を変更する
    document.body.style.backgroundColor = '#fad'; // paint のみ発生
    
    上記の例の通り「レイアウトとペイントの両方」が発生する場合と「ペイントのみ」が発生する場合があります。 余白やボーダーを変更した場合には要素の位置調整が必要なためレイアウトイベントが発生し、その後画面に表示するためにペイントイベントも発生します。 フォント色などの色のみの変更で要素の位置を変える必要がない場合には、ペイントイベントのみ発生します。

    JavaScriptからDOMを追加する
    JavaScriptからDOMを追加することで、DOMツリー、レンダーツリー、レイアウト、ペイントの各イベントが発生します。
    // ul以下にliを追加する
    var ul = document.querySelector('ul');
    var li = document.createElement('li');
    li.textContent = 'JavaScriptで挿入したli要素';
    ul.appendChild(li);
    

    ユーザー操作
    様々なユーザー操作でもレイアウトやペイントが発生します。以下にはそれらイベント発生するユーザーアクションの一例を示します。
    • スクロールする
    • ウインドウのサイズを変更する
    • :hover要素などにマウスオーバーしてスタイルが切り替わる
    • など



    ブラウザは頭が良い

    さて一つ前の章では、様々な場面でレンダリング処理が発生することが分かりました。 レンダリング処理は得てして重たい処理です。何度も頻繁に発生すると画面がカクつくことにもなってしまいます。

    ブラウザはそのような自体を避けるために、レンダリング処理の回数を減らすための最適化を行います。 最適化の一つの手法として「何もしない」または「まとめて行う」というものです。
    ブラウザはJavaScriptからのスタイルの変更要求をキューにためて最後にまとめて処理を行うことで、レイアウトとペイントの発生回数を最小限にとどめるようにします。 例えば以下のような関数があるとします。
    function changePosition () {
        var icon = document.querySelector('.icon');
        icon.style.top  = '10px';
        icon.style.top  = '20px';
        icon.style.top  = '30px';
        icon.style.left = '100px';
    }
    この関数内では合計4回スタイルを変更していますが、レイアウトとペイントは最後に1回ずつしか発生しません。 このようにブラウザはレンダリングの回数を減らすように最適化を行います。



    ブラウザのレンダリング最適化を壊さないJavaScript実装

    しかしそのような最適化を壊すJavaScriptを書くことができます。 それはJavaScript内で要素の以下のようなスタイルを読み込む場合です。
    • offsetTop/Left/Width/Height
    • scrollTop/Left/Width/Height
    • getComputedStyle
    これらのスタイルを参照(またはメソッド呼び出し)をJavaScriptから要求すると、ブラウザはその時点で最新のレンダリング結果を強制的に計算し、そしてその結果を返します。 つまり上記スタイルを参照することで、最適化のためにキューにためていたスタイル変更要求をこの時点で行ってしまうのです。

    例えば以下のようにJavaScriptを書くと、ループの度にスタイルの計算が行われます。
    var div = document.querySelector('div');
    for (var i = 0; i < 100; i++) {
        // offsetLeftを参照するたびに、スタイルの再計算が強制的に行われる.
        var offsetLeft = div.offsetLeft;
        div.style.left = (offsetLeft + 1) + 'px';
    }
    
    このような実装は、せっかくのブラウザの最適化を台無しにしてしまいます。
    このような事態を避ける方法は、要素位置の参照回数はできるだけ減らすことです。 例えば以下のように最初の1回のみ要素の位置を参照して、その後はキャッシュした情報を使うようにすることで、ブラウザの最適化を邪魔しないようになります。
    var div = document.querySelector('div');
    var offsetLeft = div.offsetLeft;
    for (var i = 0; i < 100; i++) {
        offsetLeft += 1;
        div.style.left = offsetLeft + 'px';
    }
    このようにレンダリングの仕組みを理解することで、より高速なJavaScriptを実装することができるようになります。



    最後に

    今回はブラウザのレンダリングの仕組みと、それを意識したJavaScriptの実装についてブログを書きました。 レンダリングの最適化の話は60FPSを意識した実装などもっと多くのことを書きたいところです。その内容についてはまた別の記事で言及できたらと思います。

    本ブログではフロントエンド技術を中心に発信しています。ぜひ気になったら、RSSTwitterをフォローしてみてください。最新の記事をお届けします☆
    最後までご覧頂きましてありがとうございました。



    [フロントエンド] Chrome37β変更点まとめ。DirectWriteやdialog要素など。サンプルコード付きで紹介します。

    $
    0
    0
    こんにちは、@yoheiMuneです。
    7/14にThe Chromium BlogでChrome37βがリリースされたことが公表されました。β版に搭載された機能は安定板にも反映されるので、早めにチェックしておくと良いです。 このブログではver.37での変更点をサンプルコード付きで紹介します。

    画像

    Special Thanks to https://flic.kr/p/4fs1WK




    目次




    Windows環境でDirectWriteのサポート

    Windows環境において、DirectXの機能であるDirectWriteが有効になりました。これにより高い解像度のディスプレイでも綺麗に高速に文字を表現できるようになりました。DirectWriteサポート以前は、テキストレンダリングにGDI(Graphics Device Interface)という80年代の技術が使われていました。DirectWriteを使った場合と使わなかった場合の違いは以下のスクリーンショットの通りです。
    画像引用:The Chromium Blog そしてこの変更のために開発者が何らかのコードを変更する必要はないとのこと。コードそのままに表現が綺麗になるのはありがたいですね。



    dialogエレメントのサポート

    HTML5のdialogエレメントがサポートされました。dialog要素を使うことで、デザイン可能なダイアログを表現することが可能になります。詳しい使い方はdialog element demoで確認することができますが、以下にも簡単なサンプルを提示します。

    1.dialogの定義と表示と非表示

    <!--
        dialog要素でdialogを定義します.
        dialog要素はレンダリングされません.
    --><dialog id="dialog">こんにちはdialogエレメントです。</dialog><script>
    // showメソッドとcloseメソッドで表示非表示を制御します.
    var dialog = document.querySelector('#dialog');
    dialog.show();
    setTimeout(function () {
        dialog.close();
    }, 5000);
    </script>

    2.dialogにスタイルを割り当てる

    <!--CSSでスタイルを指定することができます--><style>
    dialog {
        background-color: skyblue;
        border: 1px solid rgba(0,0,0,.3);
        box-shadow: 1px 1px 2px rgba(0,0,0,.85);
        padding: 10px 20px;
    }</style><dialog id="dialog">こんにちはdialogエレメントです。</dialog><script>
    var dialog = document.querySelector('#dialog');
    dialog.show();</script>

    3.モーダルダイアログとして表示する

    <dialog id="dialog">こんにちはdialogエレメントです。</dialog><script>
    var dialog = document.querySelector('#dialog');
    // showModalメソッドを使うと、モーダルダイアログとして表示できる.
    dialog.showModal();
    </script>
    上記以外にも、以下のようなことができます。
    • モーダルの背景色を指定する
    • ダイアログから値を返す
    • Formとの連携
    Chromeでは使えなくなったshowModalDialogの代わりとしてとても便利に使えそうです。デザインカスタマイズできるダイアログがネイティブでサポートされるのは嬉しい限りです。



    その他のリリース内容

    上記2点以外にも以下のような変更があります。

    Web Cryptography APIがデフォルトで有効に
    Web Cryptography APIが37からは標準で利用可能になりました。 これを利用することで暗号化関連の処理を行うことができます。例えばハッシュ化、シグニチャの生成や検証、そして暗号化などです。


    サブピクセルフォントスケーリングのサポート
    サブピクセルフォントスケーリングがサポートされたことで、フォントサイズを変更するアニメーションをスムーズに行うことができるようになりました。


    タッチイベントの改善
    高解像度ディスプレイにおけるタッチイベントの精度の改善が行われ、タッチイベントではIntegerではなくLong値が使われるようになりました。


    zoom-inとzoom-outのノンプレフィックス化
    CSSのカーソル指定でzoom-inzoom-outはベンダープレフィックスなしに使えるようになりました。


    CUPコア数の把握
    JavaScriptにおいてnavigator.hardwareConcurrencyを使うことで、CPUコア数を取得できるようになりました。
    console.log(navigator.hardwareConcurrency);
    // 4


    ユーザーの優先言語
    JavaScriptにおいてnavigator.languagesから、ユーザーの優先言語を取得できるようになりました。
    console.log(navigator.languages);
    // ["ja", "en-US", "en"]
    またユーザーの優先言語が変更された場合にはlanguagechangeイベントが発生するようになりました。


    CSS Shapes Module
    CSS Shapes Moduleを使うことで、四角形ではない境界でフロートさせた要素とその周辺の文字を区切ることができます。 詳しくは、Good-Looking Shapes Gallery | Web Platform Team Blog(英語)をご参照ください。英語に詳しくなくてもイメージ図も多いので、感覚を掴みやすいです。


    NPAPIの非推奨引き続き
    NPAPI(Netscape Plugin Application Programming Interface)は以前のアナウンス(英語)の通り、非推奨です。セキュリティ問題とコードの複雑化が大きな問題のためです。


    Windowsにおけるmonospaceのデフォルト変更
    Windows環境におけるmonospace(等幅フォント)のデフォルトが、Courier NewからConsolasに変更されました。



    最後に

    35β変更点36β変更点に引き続き今回もChromeベータ版の変更点を記載致しました。ぜひ詳しい内容は、The Chromium Blog(元記事、英語)もご参照ください。 変更点を追うことで、様々な技術要素を知ることができて本当に良いなぁと感じています。

    最後になりますが、本ブログではフロントエンド技術を中心に発信しています。気になったら、RSSTwitterをぜひフォローしてみてください。最新の記事をお届けします☆
    最後までご覧頂きましてありがとうございました。



    [フロントエンド] jQueryのカスタムビルド機能を用いて、小さなサイズのjQueryを使おう!

    $
    0
    0
    こんにちは、@yoheiMuneです。
    Web業界で知らない人はいないんじゃないだろうかというjQuery。実はjQueryの機能を選んで自由にカスタマイズできるって知ってますか? 今日はjQueryのカスタムビルドを用いて、サイズを減らす試みを紹介したいと思います。

    画像

    Special Thanks to https://flic.kr/p/7fADmS




    目次




    jQueryのカスタムビルド

    この手順説明は、執筆時点で最新のjQuery2.1.1での説明となります。

    jQueryは通常、jquery.com/downloadから取得することができます。 しかしjQueryの開発はGithub上で行われており、そちらを利用することでjQueryを自分でもビルドすることもできてしまいます。 そしてそのビルドではカスタムビルドがサポートされており、制作するサイトで必要になる機能のみでビルドすることが可能です。 不要な機能を省くことでサイズを削減することができ、便利なjQueryをより小さいサイズで利用することが可能です。 これは、特にAndroidやiPhoneといったネットワークの弱いモバイル向けサイトには効果が高いものです。
    今回はそのカスタムビルドを紹介します。まずはjQueryをビルドする手順を紹介します。



    jQueryを自分でビルドする

    jQueryを手動でビルドするには以下の2ステップで行います。

    1. Githubからダウンロード

    まずはjQueryの開発コードをGithubからダウンロードします。
    $ git clone git://github.com/jquery/jquery.git
    ダウンロードしたらカレントディレクトリに「jquery」というディレクトリができているので、そちらに移ります。
    $ cd jquery


    2. ビルドを行う

    そしてjQueryのビルドを行うには、以下のコマンドを実行します。
    $ npm run build
    この処理では、必要なnpmモジュールをインストールして、Gruntのビルドタスクを実行します。 これらの処理を実行するためには、nodenpmgruntの設定が必要ですので、無い方はそれらを導入してください。

    以上を実行するとdist/ディレクトリにビルド後のモジュールが置かれます。 これで手動でのビルドができました。

    次には本題のjQueryのカスタムビルドを説明します。



    jQueryのカスタムビルドを行う

    上記では多くのサイトで利用されている全機能を持つjQueryのビルドを紹介しました。 ここでは自由に機能を削減できるカスタムビルドについて説明します。


    削減できる機能一覧
    カスタムビルドではcoreselectorを除く全機能を削減することができます。 削減するためにはGithubページsrcフォルダ以下で、削減したい対象を「.js」抜きで指定します。
    例えば以下のようなモジュールを削減することができます。
    機能名説明
    ajaxAjaxに関するすべての機能を削除します。$.ajax()$.get()$.post()$.ajaxSetup.load()、transportsや、.ajaxStart()といったajax関連のイベントの省略名を削除することができます。
    ajax/xhrXMLHTTPRequest AJAXのみを削除することができます。
    ajax/script<script>によるAjax機能を削除することができます。
    ajax/jsonpJSONPによるAjax機能を削除することができます。
    css.css()とアニメーションしない.show().hide().toggle()を削除することができます。またcssモジュールに依存するeffects、dimensions、offset、といったモジュールも削除されます。
    deprecated非推奨メソッドでまだ削除されていないメソッドを削除することができます。現在のところ.andSelf()のみです。
    dimensions.width().height()を削除することができます。inner-outer-といったバリエーションのものも削除されます。
    effects.animate()やその省略版の.slideUp().hide("slow")を削除することができます。
    event.on().off()などのイベント関連のすべての機能を削除することができます。またevent/aliasも削除されます。
    event/alias.click().mouseover()といったイベントのすべてのショートハンドを削除することができます。
    offset.offset().position().offsetParent().scrollLeft().scrollTop()メソッドを削除することができます。
    wrap.wrap().wrapAll().wrapInner().unwrap()を削除することができます。
    core/readyscriptファイルをbody閉じタグの直前に置いてある場合には、このredyモジュールを削除することができます。これを削除するとjQuery(document).ready()はnot functionとなり、.on("ready", ...)などのイベントは発火しなくなります。
    deferredjQueryのディファード機能を削除することができます。またjQuery.Callbacksも削除されます。注意すべき点はdeferredを削除しても、これに依存するajax、effects、core/readyは存続し続けます。それらモジュールを削除するか、それらを利用する場合にはjQuery.Deferredの代替となるモジュールをロードする必要があります。
    exports/globalwindowオブジェクトに$やjQueryといったグローバルオブジェクトをアタッチしないようになります。
    exports/amdAMD定義を削除します。
    sizzlejQuery独自のsizzleエンジンを削除することができます。sizzleエンジンの代わりに、ブラウザのquerySelectorAllが利用されます。
    次に実際にカスタムビルドを行う方法を説明します。


    カスタムビルドを実行する
    カスタムビルドを行うためには、gruntタスクのcustomを利用します。例えばajax機能を削除したい場合には、以下のように行います。
    $ grunt custom:-ajax
    上記の場合はajaxモジュールが削除された結果がdist/以下に生成されます。 ただ、customタスクだけだとどれだけサイズが減ったのかが分かりづらいので、compare_sizeタスクも一緒に実行すると、ダイエット効果が見えてよいです。
    $ grunt custom:-ajax compare_size
    Running "custom:-ajax" (custom) task
    Creating custom build...
    
    
    Running "build:all:*:-ajax" (build) task
    -ajax
    -ajax/jsonp
    -ajax/load
    -ajax/parseJSON
    -ajax/parseXML
    -ajax/script
    -ajax/var/nonce
    -ajax/var/rquery
    -ajax/xhr
    -manipulation/_evalUrl
    -event/ajax
    >> File 'dist/jquery.js' created.
    
    Running "uglify:all" (uglify) task
    File dist/jquery.min.map created (source map).
    File dist/jquery.min.js created: 215.98 kB → 74.9 kB
    
    Running "dist" task
    
    Running "compare_size:files" (compare_size) task
       raw     gz Sizes                                                            
    215984  63685 dist/jquery.js                                                   
     74896  25789 dist/jquery.min.js                                               
    
       raw     gz Compared to master @ 995f70777ac6c0f988a44807ef1399e73937b2ee    
    +23698  +6191 dist/jquery.js                                                   
     +7578  +2147 dist/jquery.min.js                                               
    
       raw     gz Compared to last run                                             
    +23698  +6191 dist/jquery.js                                                   
     +7578  +2147 dist/jquery.min.js                                               
    
    Saved as: master
    
    Done, without errors.
    
    ajaxモジュールを削除した場合のjquery.min.jsのgzip後のサイズは、25789Bytesということが分かります。

    次にはそれぞれのモジュールを削除した場合に、どれだけのダイエット効果があるのかを見ていきましょう。


    各モジュールのダイエット効果を見てみる
    ここでは各モジュールを削除した場合のダイエット効果を検証します。どれくらいのシェイプアップされるのでしょうか。

    単位はBytesです。

    削除対象jquery.jsjquery.min.jsjquery.min.js(gzip)
    フルバージョン2461978416329438
    -ajax2159847489625789 (-3649)
    -ajax/xhr2428468311829048 (-390)
    -ajax/script2450028356129258 (-180)
    -ajax/jsonp2438038334029161 (-277)
    -css1993996812223769 (-5669)
    -deprecated2460608412029419 (-19)
    -dimensions2445408366829282 (-158)
    -effects2261647656326744 (-2694)
    -event2206917451726235 (-3203)
    -event/alias2451818354529236 (-202)
    -offset2409198223528805 (-633)
    -wrap2448568343529263 (-175)
    -core/ready2439098352529232 (-206)
    -deferred2418658274228980 (-458)
    -exports/global2456778405129390 (-48)
    -exports/amd2452638410929417 (-21)
    -sizzle1922866731823642 (-5796)
    ものによってダイエット効果が違いますが、必要機能のみに絞り込むと少しサイズの小さいjQueryを使えそうです。

    次には案件でも利用しているカスタムビルドを紹介します。


    カスタムビルドのサンプル
    例えばAndroidとiPhone向けのWeb制作の場合、例えば以下のようなカスタムビルドを行うことができます。
    # ajax/script, ajax/jsonpはあまり使わない
    # cssはclassNameでやり取りするか、elm.styles.xxxを使う
    # deprecatedは使わない
    # effectsは今風にcss3アニメーションを使う
    # event/aliasはやめて、onとoffに統一
    # wrapはあまりつかない
    # querySelectorAllでできる範囲で十分
    
    $ grunt custom:-ajax/script,-ajax/jsonp,-css,-deprecated,-effects,-event/alias,-wrap,-sizzle compare_size
    
    ## 出力は一部省略 ## 
    Running "compare_size:files" (compare_size) task
       raw     gz Sizes                                                            
    139871  42005 dist/jquery.js                                                   
     48596  17118 dist/jquery.min.js   
    
    この場合だとgzip後のサイズは171,18Bytesで、フルバージョンから12,320Bytesの削減をすることができます。 モバイルであればこの約12KBの削減は大きなもので、初期表示のパフォーマンスに大きく貢献してくれます。



    最後に

    今日はjQueryのカスタムビルドについて紹介しました。 jQueryのカスタムビルドをプロジェクトに用いる場合は、メンバーのスキルが足りているか、メンバーと共通認識を持てているかが重要となります。 開発効率を落とさずかつサイズを削減できるところを探して、プロジェクトに合ったカスタマイズをしてみてください。

    また近年のブラウザであれば、jQueryで良く用いるイベントのOn/OFF、Ajax、クラスの付け替えはちょっとした実装で実現することができます。 プロジェクトに余裕があればjQueryを使わない選択肢も良いかもしれないですね!

    今後も本ブログでは、フロントエンドに関する情報を書きたいと思います。気になった方はぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。
    最後までご覧頂きましてありがとうございました!



    [フロントエンド] 未来を引き寄せるPolymerを理解しよう

    $
    0
    0
    こんにちは、@yoheiMuneです。
    本日は、Polymerという未来を先取りしたライブラリを理解するためにブログを書きます。 少し長いですが、最後まで読むとPolymerへの理解が進むと思いますので、お付き合い頂けたら幸いです。

    画像

    Special Thanks to https://flic.kr/p/5aDA79




    目次




    Polymerとは何か

    PolymerはGoogleが開発しているライブラリの1つであり、polymer-project.orgで公開されています。

    Polymerの一番大きな特徴は、Bootstrap、Angular.js、jQueryといった今までのライブラリとは性質が異なるということです。 BootstrapやjQueryといったライブラリは現在広く普及した技術を使っているのに対して、PolymerはWebComponentsやデータバインディングといった新しい(そして多くのブラウザではまだ完全には実装されていない)技術を利用しています。

    新技術ばかりを使ったライブラリということは、実戦投入はまだまだ難しいのでしょうか。 まぁ大変だと思います。意図しない挙動が起きた時に面と向かって対応する必要があるし、IE9などのサポートはないからです。
    しかしPolymerのFAQによれば、Polymerは"developer preview"であるにも関わらず、いくつかのプロダクションにも取り込まれているようです。

    なぜGoogleはこのような最新技術を使って、多くのブラウザで完全に快適には動かないライブラリを作っているのでしょうか。 それはGoogleがChromeブラウザを開発して、またWeb標準を強く推進しているからだと思います。 新技術を開発する上で、実際に使われたというユースケースからのフィードバックはとても大切です。そのフィードバックを得るためのプロトタイプとしての側面が、Polymerにはあるようです。

    実際にGoogleの人のブログでもそのような見解が示されていました。ソースコードはGithubで公開され、そちらへのバグレポートも受け付けているので、Google以外のコミュニティからも多くのフィードバックを受けて、より良いWeb技術を作って行こうというためのライブラリのようです。

    さぁみんなで新技術の担い手になろう!...という前に、Polymerについての理解を深めることにします。 Polymerはどのような最新技術を使っているのでしょうか。次の章ではそれぞれについて少し見ていきましょう。



    Polymerが扱う新技術

    上記では、Polymerが新技術を扱っていることについて触れました。 具体的にはどのような技術要素なのでしょうか。 この章ではPolymerが扱う新技術について触れたいと思います。
    Polymerは以下のような技術要素を扱っています。

    • Web Components
    • データバインディング
    • Web Animations
    • Pointer Events

    以下ではそれぞれの技術要素について簡単に説明します。


    Web Components
    Web ComponentsはPolymerにおいて最も重要な技術要素です。
    Web ComponentsはWebの世界にスコープを概念を本格的に持ち込む仕様で、これにより独立性の高いモジュールを作る事ができるようになります。 Web Componentsは、HTML ImportTemplateCustom ElementsShadow DOMの4つの仕様で構成されています。 詳細は@agektmrさんのブログ(日本語)をぜひ読んでみてください。


    データバインディング
    Two Way BindingやModel Driven Viewとも呼ばれる仕組みで、JavaScriptとDOMとの間でデータの同期を自動的に行う機能です。 JavaScriptの値を変更したらDOM上の値が自動的に更新され、DOM上の値が変わったらJavaScriptに自動的に代入されるといった具合です。 JavaScriptの変更監視にはObject.observeを利用し、DOMの変更監視にはMutationObserverを利用します。
    Polymerにおけるデータバインディング詳細は、Data binding overview(英語)をご確認ください。


    Web Animations
    上記2つの技術要素で画面表示を行うことができますが、動きはありません。UI/UXの向上や意味の伝達を行う上で、Animationはとても重要です。 Webにおけるアニメーションは、CSS3 TransitionsCSS3 AnimationsSVG1.1がありますが、Web Animationsはそれら3つを抽象化するアニメーションモデルを提供しています。
    PolymerにおけるWeb Animationsの詳細は、Web Animations(英語)をご覧下さい。


    Pointer Events
    マウスイベントとタップイベントは似て非なるものです。 近年のWebアプリケーションの多くはマウス操作とタップ操作の両方をサポートしていますが、その実装はなかなか大変なものです。 Pointer Eventsでは、それら2つの操作を統一して扱うための仕様です。 PolymerにおけるPointer Eventsの詳細は、Pointer Events(英語)をご覧下さい。


    以上の技術要素がPolymerで主に利用されるものです。 新しい技術ばかりで聞き慣れないものもあるかもしれません。 それぞれの技術要素が各ブラウザーでどれほど実装されているのかは、Can I Useでサクッと確認することができますが、まだまだ全ブラウザで使えるくらい普及していないことがすぐに分かります。

    次の章ではPolymerの構成を説明します。Polymerの構成はどのようになっていて、ブラウザの実装差異にどのように対応しているのかを理解することは、Polymerの理解に役立ちます。



    Polymerのアーキテクト

    この章ではPolymerの構造について説明したいと思います。 Polymerの全体像を以下に示します。
    画像
    Red Layer
    赤い層は上記で触れたPolymerの利用している技術要素です。それらをまだ未実装のブラウザでも使えるように、platform.jsがあり、新技術のポリフィル(polyfill = ブラウザに実装されていない技術をJavaScriptとかで擬似的に再現すること)として機能します。
    Yellow Layer
    Polymerにおける便利な機能を提供しています。これには各種技術をどのように組み合わせて使うべきかに対する制作者の意見/考えが含まれています。例えばCustom Elementにはデータバインディングの技術を組み合わせると使いやすい、とかです。データバインディング、独自のシンタックス、変更の監視など、アプリケーションをより楽に作る上で必要なことが実装されています。
    Green Layer
    Polymerで作成された機能やUI要素が含まれます。ツールバーやボタンといった表示要素や、core-ajaxといった機能的なものも存在します。
    Blue Layer
    Polymerを用いたサンプルアプリです。図内のpica以外にも計算機TOPEKAなどのサンプルが存在します。
    以上のように、Polymerはいくつかの役割に分けることができます。 Polymerを理解する上で、上記のアーキテクチャの理解はとても重要です。 読んでいる資料やリファレンスがPolymerにおいてどの立ち位置のものなのかが分かれば、理解が進みますね!

    次の章では実際にPolymerを使ったアプリケーションを作って、理解を深めたいと思います。



    Polymerを使ってサンプルアプリを作ってみる

    この章ではPolymerを実際に使って、サンプルアプリケーションを作ってみたいと思います。 ここで紹介する内容は、Polymerのチュートリアル(英語)の内容をかいつまんで説明しますので、詳細はチュートリアル本体をご確認ください。

    まず始めにPolymerを利用するために、チュートリアルページから必要なモジュールをダウンロードしてきます。
    ダウンロードしたら、表示確認要にサーバーにデプロイします。ここではPythonの機能を使ってサクッとサーバーを立てることにします。
    # ダウンロードしたフォルダに移動
    cd polymer-tutorial-master
    
    # Python 2.xの場合
    python -m SimpleHTTPServer 
    
    #Python 3.xの場合
    python -m http.server 
    
    これで、以下のURLでアプリケーションにアクセスすることができます。
    http://localhost:8000/finished/
    さてチュートリアルを進めていると、以下のようなタブを作る場面があります。Polymerを使うと、こんなにも簡単にタブを作ることができていいですね!
    <html><head><!--新技術を使うためのポリフィル--><script src="../components/platform/platform.js"></script><!--ヘッダーパネルのインポート--><link rel="import" href="../components/core-header-panel/core-header-panel.html"><!--ツールバーのインポート--><link rel="import" href="../components/core-toolbar/core-toolbar.html"><!--タブのインポート--><link rel="import" href="../components/paper-tabs/paper-tabs.html"><!--表示する要素の装飾をカスタマイズします--><style>
                html,body {
                  height: 100%;
                  margin: 0;
                  background-color: #E5E5E5;
                  font-family: 'RobotoDraft', sans-serif;
                }        
                core-header-panel {
                  height: 100%;
                  overflow: auto;
                  -webkit-overflow-scrolling: touch; 
                }
                core-toolbar {
                  background: #03a9f4;
                  color: white;
                }
                #tabs {
                  width: 100%;
                  margin: 0;
                  -webkit-user-select: none;
                  -moz-user-select: none;
                  -ms-user-select: none;
                  user-select: none;
                }</style></head><!--unresolvedを付けることで表示モジュール準備中の状態は表示しない--><body unresolved><!--画面上部にツールバーを作り、その中にタブを配置した例--><core-header-panel><core-toolbar><paper-tabs id="tabs" selected="all" self-end><paper-tab name="all">ALL</paper-tab><paper-tab name="favorites">FAVORITES</paper-tab></paper-tabs></core-toolbar></core-header-panel><!--タブ切り替わりのイベントをキャッチする処理の例--><script>
              var tabs = document.querySelector('paper-tabs');
              tabs.addEventListener('core-select', function() {
                console.log("Selected: " + tabs.selected);
              });</script></body></html>
    ちょっと長くなってしまいましたが、タブを作るために<paper-tabs><paper-tab>の2つだけで簡単にタブができてしまうのは驚きです。 上記の表示は以下のようになります。
    画像 Web上におけるタブの実装はなかなか面倒ですが、このようにコンポーネントができていると嬉しいですね。
    チュートリアルではカードのような表示や、サーバーからJSONを取得する処理などを学ぶことができます。気になった方はぜひやってみてください。

    Polymerには標準で様々なUIが用意されていますが、自分でも要素を作成することができます。 次の章では独自のタグを作成してみたいと思います。



    Polymerを拡張する

    Polymerでは標準で用意された要素以外に、独自に要素を作成することができ、それぞれのアプリケーションや組織に特化したカスタムエレメントも作成することができます。 この章では独自のエレメントを作成してみたいと思います。

    あるサイトでアバターを表示する場合に、名前付きで以下のように表示されるようにしたいとします。
    <div><img src="path/to/image.png"/><p>Yohei Munesada</p></div>
    これを以下のようなカスタムエレメントで表示できるようにします。
    <ym-avatar src="path/to/avatar.png" credit="Yohei Munesada"></ym-avatar>
    Polymerを使って独自のカスタムタグを作るには、以下のように実装します。
    <!--ファイル名:ym-avatar.html--><!--独自タグを定義するために、polymer.htmlを読み込みます--><link rel="import" href="./components/polymer/polymer.html"><!--polymer-elementタグを使って、独自タグを定義します--><polymer-element name="ym-avatar" attributes="src credit"><!--templateを用いて中身を定義します--><template><!--ここで定義されたスタイルは、この要素にのみ適用されます--><style>
            div {
                width: 30%;
                margin: 10px auto;
                background-color: rgba(255,255,0,.3);
                border-radius: 8px;
                padding: 10px;
                box-shadow: 2px 2px 2px rgba(0,0,0,.25);
            }
            img {
                width: 100%;
            }
            p {
                text-align: center;
            }</style><!--中身の構造を定義します--><div><img src="{{src}}"/><p>{{credit}}</p></div></template><!--カスタムタグを定義します--><script>
            Polymer('ym-avatar');</script></polymer-element>
    上記で定義した独自要素は以下のように利用します。
    <!doctype html><html><head><!--ポリフィルを読み込み、各種ブラウザ対応します--><script src="../components/platform/platform.js"></script><!--上で定義した独自タグを読み込みます--><link rel="import" href="./ym-avatar.html"></head><body unresolved touch-action="auto"><!--独自タグを利用します--><ym-avatar src="../images/avatar-01.svg" credit="Yohei Munesada"></ym-avatar></body></html>
    表示してみるとこんな感じになります。
    画像 またDevツールで見てみると、独自に定義された要素があり、その中にShadow DOM(Web Componentsの技術要素の1つ)があることが分かります。
    画像 こんな感じで独自要素を定義し、利用することができます。 サイト全体や会社全体(または世界中で使える)便利な要素を定義できると素敵ですね!!



    余談:PolymerとMaterial Design

    Google IO 2014のKeynoteで発表されたMaterial Design。PolymerではMaterial Designの適用された各種ペーパー要素を利用することができます(サンプルはこちら)。なぜPolymerでMaterial Designの実現をしているのでしょうか。執筆時点(2014/8/21)のFAQに以下のことが記載されていました。

    How is Polymer related to material design?

    Polymer is the embodiment of material design for the web. The Polymer team works closely with the design teams behind material design. In fact, Polymer played a key role in material design’s development: it was used to quickly prototype and iterate on design concepts. The material design components are still a work in progress, but will mature over the coming months.

    (日本語訳)
    PolymerとMaterial Designとの関係は?
    PolymerはWebにおいてMaterial Designを実現しているライブラリです。 PolymerチームはMaterial Designのデザインチームの近くで働いていて、実際にPolymerはMaterial Designの開発において重要な役割を果たしています。 Polymerは素早いプロトタイプとして利用され、デザインコンセプトを繰り返し適用しています。 Material Designを用いたコンポーネントは現在進行中ですが、数ヶ月で成熟したものとなるでしょう。



    PolymerはMaterial Designに対してもプロトタイプとしての振る舞いを持ち、有益なフィードバックを返すことを1つの目的にしているようですね。



    最後に

    本ブログでは、未来を作るPolymerについて扱いました。 Polymerって何とか、理解しづらいとよく話を聞きます(そして以前の自分もそうでした)。 本ブログはPolymerを理解するためのきっかけになればと思い書きました。 みなさまのPolymerに対する理解に少しでも貢献できていれば幸いです。

    Polymerには本ブログで触れられなかった素晴らしいことがまだいっぱいあるので、ぜひpolymer-project.org/を覗いてみてください。

    今後も本ブログでは、フロントエンドに関する情報を書きたいと思います。気になった方はぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。
    最後までご覧頂きましてありがとうございました!




    [その他] JJUG納涼会でLTしてきました

    $
    0
    0
    こんにちは、@yoheiMuneです。
    先日、JJUGナイトセミナー「ビール片手にLT&納涼会」に参加して、LTしてきました。 今日はそのLTに込めた思いについてブログを書きたいと思います。

    画像


    JJUG納涼会

    JJUG納涼会では、ピザを食べたりビールを飲んだりしながら、LTを聞くというカジュアルな形式でした(お酒苦手な自分にもコーラがあった、優しさを感じますw)。 アジェンダはDoorKeeperで公開されている通りだいたい20本で、各5分ずつでした。 山本裕介さん動画をYoutubeにまとめてくれています。 恥ずかしいけど自分のLTを見返して振り返りしよう・・・。

    前職ではJavaエンジニアだったので、久しぶりのJJUG参加にうきうきしていました。 案の定、JJUGのLTは温かさに包まれていますね。Twitterでまさかり投げられたり、LT中に突っ込みを受けたりする光景を見ていて、本当に楽しめました。 もしお時間あえば参加してみることをお勧めしますー。


    LTしてきた内容と目的

    今回のLTはお題と形式が何でもいいということだったので、「ある男がJavaエンジニアからフロントエンドエンジニアになった話」をしてきました。 つまりは、自分が前職から現職に転職する経緯とかステップとかですw。


    さて今回のLTではいくつかの目標を設定していました。それは、

    • 1秒の狂いもなく5分間ジャストで終わる
    • プレゼンする限りは聴衆に最大限貢献する
    • トークスクリプトを書く
    • プレゼンの練習に時間を費やす
    • プレゼンのトークを鍛える
    • 1分間に350文字をハキハキと話せるようになる
    • プレゼンで会場の雰囲気を作る
    • など・・・

    いっぱい書いてしまいましたがつまりは「1回1回のプレゼンでしっかりと聴衆の方に貢献する」ことを目標にしていました。
    今回のLTでは「語り部LTで制限時間5分ジャストを目指す。そのためにトークの練習をめっちゃやる」ということでした。 そして会場の温かい雰囲気にも助けられ5分ジャストのプレゼンをすることができました、皆様に感謝です。

    Javaのコードが1行も出てこなくてすみません。でもでも、参考になったという声も聞けて嬉しかったです!JJUGはやっぱり良いですね、また遊びに行かせてくださいm(。。



    最後に

    JJUGに参加して、よしJava8をやろう、という気持ちになりました。Java8をやりたい!新しい技術を学ぶのは楽しいですね、らむだ♪すとりーむ♪

    今本ブログでは、通常はフロントエンドに関する情報を書いています。気になった方はぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。
    最後までご覧頂きましてありがとうございました!



    [フロントエンド] Chrome 38ベータ。変更点などまとめ。

    $
    0
    0
    こんにちは、@yoheiMuneです。
    8月末にChromium BlogでChrome 38βが発表されましたね。半月くらい遅れましたが、更新内容をブログに書きたいと思います。今回も盛りだくさんです。

    画像


    目次




    Chromeベータ版の入手、新機能の動作設定

    Chromeのβ版は、以下から入手することが可能です。
    https://www.google.com/intl/en/chrome/browser/beta.html

    また、追加された機能によっては、chrome://flagsから「JavaScriptの試験運用機能を有効にする」を有効にする必要があります。



    ユーザー管理機能

    Chrome上のユーザーを管理するUIが綺麗になりました。 ユーザー機能については、こちらのブログをご確認ください。 下記スクリーンショットのような、より直感的なGUIでユーザーの切替、追加などが行うことができます。
    画像 また、ゲスト機能が追加されたようです。家族や友達に少しだけPCを貸す場合に、自分のアカウントを守りつつ、使った人の履歴を残さないということができるようです。
    ユーザー管理機能の変更内容は、窓の社で詳しく紹介されてました。



    <picture>の追加

    picture要素がサポートされました。これは例えば以下のような記述をして、複数の画面サイズや密度に対応するために利用できます。
    <picture><source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x"><source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x"><img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" ></picture>
    picture要素はレスポンシブデザインの概念を持ちます。今までであれば複数の画面サイズに対して、CSSのメディアクエリやJavaScriptでの表示出し分けをしていました。 picure要素を使うことで今度はHTMLでも複数の画面サイズに対して画像の出し分けをすることが可能です。



    新しいJavaScriptの仕様を追加サポート

    ECMAScript6の仕様に含まれるいくつかの機能がサポートされました。

    • とても要望が多かったMapとSetがとうとう実装されたようです。これらのデータ構造を用いることで、より簡単により合理的にプログラミングを行うことができます。
    • イテレーター(Iterator)がサポートされました。それを用いることで配列や文字列といった連続的なデータをイテレーションできるだけでなく、MapやSetもイテレーションできるようになりました(詳細)。
    • Symbolがサポートされました。Symbolは数値や文字列といったプリミティブ型データのラッパークラスで、唯一で普遍な(immutable)なオブジェクトで、オブジェクトのプロパティなどに利用することができます。
    • Math.signMath.log10といった機能がMathオブジェクトに追加され、同様の機能を各実装者が個別に実装する必要がなくなりました。サポートされた機能一覧はこちらから確認することができます。

    今後のChromeアップデートでもECMAScript6の仕様がサポートされていくとのこと。楽しみですね。



    その他のアップデート項目

    上記以外にも以下のような機能が追加されました。

    • Network Information(NetInfo))APIが利用できるようになりました。Android、iOS、ChromeOSにおいて、navigator.connection.typeという簡単なコードで現在のネットワーク種別(Wifiとか)を認識することができます。これを用いることでWifi時のみデータを同期するなどの動きを実現することが可能です。
    • Screen Orientation APIがサポートされました。これを用いることで横向き(landscape)や縦向き(portrait)に合わせた画面表示ができる他に、画面の向きを固定することできます。
    • CSSのimage-renderingにおいてpixelatedプロパティがサポートされました。image-renderingを用いることで画像をオリジナルとは異なるサイズで描画する際に、どのようなアルゴリズムで拡大/縮小するかを指定することができます。pixelatedのサンプルがGithubに用意されています。
    • Encode APIがサポートされバイナリーデータのエンコード/デコードがChromeネイティブで行うことができるようになりました。例えばTextDecoderを用いてArrayBufferから文字列への変換ができるようです。
    • 新しいFileインターフェースがサポートされ、Fileの新規作成ができたり、Blobと同じようなインターフェースでFileを扱えるようになりました。



    最後に

    Chrome38のアップデートではユーザー機能強化が日本の記事では良く紹介されていますが、JavaScriptやCSS周りの新機能も盛りだくさんですね。 一つ一つ使ってみてWebの最先端を味見して行きたいと思います。Chromeのアップデートを追うと新しい機能に出会えるので、毎回のアップデートが楽しみです。

    今後も本ブログでは、フロントエンドに関する情報を書きたいと思います。気になった方はぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。
    最後までご覧頂きましてありがとうございました!



    [JavaScript] ECMAScript6のMapとSetを使ってみよう

    $
    0
    0
    こんにちは、@yoheiMuneです。
    今日は、ECMAScript6で定義されているMapSetを使ってみたいともいます。Chromeはver.38から使えるので、すぐそこまできている技術です。

    画像

    Special Thanks to https://flic.kr/p/aBtMu5



    目次




    Map

    多くのプログラミング言語でサポートされているMapが、とうとうJavaScriptでも使えるようになりました。 Mapはコレクション型の1つで、データを格納するためのデータ構造です。 JavaScriptではオブジェクトリテラルを用いて、以下のようにデータを保持することができます。
    var data = {
        name: 'Yohei',
        sex: 'male',
        contry: 'JP'
    };
    Mapでも似たようにKey-Value構造でデータを格納することができます。
    // Mapオブジェクトの作成
    var map = new Map([
        ['one', 'My key is a string.'],
        ['1', 'My key is also a string'],
        [1, 'My key is a number'],
        [document.querySelector('#log'), 'My key is an object']
    ]);
    
    そして上記を見ると分かる通り、Keyには文字列以外にも数値やオブジェクトなどを指定することができます。 これは小さなことのように見えますが、かゆいところに手が届く感じで、ありがたい機能です。

    Mapの機能をさらっと確認しましょう。以下のようなコードを利用することができます。
    // Mapの生成(初期値なし)
    var map = new Map();
    
    // Mapの生成(初期値あり)
    // 2次元配列で値を指定するんですね...
    var map = new Map([
        ['one', 'My key is a string.'],
        [1, 'My key is a number']
    ]);
    
    // 値の取得
    map.get('one'); // => "My key is a string."
    
    // 値の設定
    map.set('two', 'aaa');
    
    // 値の設定(キーが同じものは上書きされます)
    map.set('two', 'aaa');
    map.set('two', 'bbb');
    map.get('two'); // =>bbb
    
    // 格納したデータ数
    map.size; // => 3
    
    // 存在確認
    map.has('one'); // => true
    
    // データの削除(1つ)
    map.delete('one');
    
    // データの削除(全部)
    map.clear();
    
    // keyを全部取得
    var keys = map.keys(); // => MapIteratorが取得できる
    // for of 構文はIteratorを回すことができる
    for (var key of keys) {
        console.debug(key);
    }
    
    // 値の取得(values)
    var values = map.values(); // => MapIteratorが取得できる
    for (var value of values) {
        console.debug(value); // => "My key is a string." とか
    }
    
    // 値の取得(entries)
    var entries = map.entries();
    for (var entry of entries) {
        console.debug(entry); // => ["one", "My key is a string."] とか
    }
    
    // 値の取得(forEach)
    map.forEach(function (value, key, map) {
        console.debug(value, key, map); // => My key is a string. one Map {}  とか
    });
    
    Key-Value構造のデータ構造で、それに合わせた様々なインターフェースがあるのは、とてもありがたいなぁと思う今日この頃です。 Objectでデータを格納するよりもmap.keys()map.has()など使い勝手が良いと感じます。

    またMapの各ブラウザの実装状況は、kangax.github.ioをご覧頂くと把握することができます。



    Set

    Setでは、重複しない一意なデータのコレクションを扱うことができます。 ここで言う「一意な」という定義は後ほど扱います。 似たようなデータ構造に配列がありますが、配列では重複したデータ(同じデータ)を複数格納できる点が異なります。
    Setについても例を見てみたいと思います。
    
    // 初期化
    var set = new Set();
    
    // 値付きの初期化
    // 数値、文字列、関数、など色々と入る.
    var set = new Set([1, 'one', function (){}, document.body]);
    
    // 値の追加
    set.add(2);
    
    // 値の存在確認
    set.has('one'); // => true
    
    // 値の削除
    set.delete(1);
    
    // 格納してあるデータ数
    set.size; // => 4
    
    // 値の取得(values)
    for (var value of set.values()) {
        console.debug(value); // => 'one'、など
    }
    
    // 値の取得(entities)
    for (var entry of set.entries()) {
        console.debug(entry); // => ['one', 'one']、など
    }
    
    // 値の取得(forEach)
    set.forEach(function (value1, value2, set) {
        console.debug(value1, value2, set); // => one one Set
    });
    Setの最も便利なところは「値が重複しない」という特徴だと思います。 例えば以下のように、APIレスポンスをサーバーから受け取った場合に、ユニークユーザー数は何人かを知る時に便利に使えそうです。
    getDataFromAPI(function (results) {
        var set = new Set();
        results.forEach(function (result) {
            set.add(result.userId);
        });
    
        var numOfUser = set.size;
        alert('ユニークユーザー数は' + numOfUser + 'です');
    });
    
    上記の場合にset.add(result.userId)で、UserIdが重複している場合にも気にせずaddできる点が素敵です。 Setも使い勝手の良いコレクションAPIなので、ぜひぜひ使えるようになりたいところです。



    同一性の定義

    MapのキーやSetの値は、重複を許さないという話が上記でありましたが、重複とは具体的にどういう定義なのでしょうか。 ECMAScript6の仕様書によると、SameValueZeroのアルゴリズムが使われるようです。ここでは簡単に引用して、ご紹介させて頂きます。
    1. ReturnIfAbrupt(x).
    2. ReturnIfAbrupt(y).
    3. If Type(x) is different from Type(y), return false.
    4. If Type(x) is Undefined, return true.
    5. If Type(x) is Null, return true.
    6. If Type(x) is Number, then
      6.1. If x is NaN and y is NaN, return true.
      6.2. If x is +0 and y is -0, return true.
      6.3. If x is -0 and y is +0, return true.
      6.4. If x is the same Number value as y, return true.
      6.5. Return false.
    7. If Type(x) is String, then
      7.1. If x and y are exactly the same sequence of code units (same length and same code units in corresponding positions) return true; otherwise, return false.
    8. If Type(x) is Boolean, then
      8.1. If x and y are both true or both false, then return true; otherwise, return false.
    9. If Type(x) is Symbol, then
      9.1. If x and y are both the same Symbol value, then return true; otherwise, return false.
    10. Return true if x and y are the same Object value. Otherwise, return false.
    引用:http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero

    ECMAScriptの仕様書は初めて中身を見ましたが、こんな感じで定義されているんですね。 意外と読みやすいなぁと思いました。



    参考資料

    MapとSetについては、以下の記事を参考にしました。

    Collecting and Iterating, the ES6 Way | HTML5Rocks

    Map | MDN

    Set | MDN

    最後に

    Chrome38からMapSetが使えるようになったので、それぞれのAPIを調べてみました。 APIの紹介だったので、ちょっと地味でしたねw。

    今後も本ブログでは、フロントエンドに関する情報を書きたいと思います。気になった方はぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。
    最後までご覧頂きましてありがとうございました!



    [フロントエンド] 第3回トリトンJSで、Material DesignとPolymerについて話してきました。プレゼン資料付きです。

    $
    0
    0
    こんにちは、@yoheiMuneです。
    先日の第3回 HTML5minutes! 〜triton-js〜で30分間のキーノートを担当させて頂きました。 今日はその内容をご紹介したいと思います。




    Material Design と HTML5

    今回のセッションでは以下の内容を扱いました。
    • Material Designについて
    • Polymerというライブラリについて
    • Polymerを使ってMaterial DesignをWebで実現する実装について
    • Paper Elementの実装の味見
    Material DesignGoogle IO 2014で発表されたGoogleのデザインコンセプトです。 今回のセッションではMaterial Designを紹介する動画にまずは触れた後に、Material Designのゴールやコンセプトや構成要素に触れ、Material Designをまるっと学ぶことを目的にしました。

    その後のPolymerでは「Google製の未来を作るライブラリである」ことを説明し、jQueryなどの既存ライブラリとは違う一面を持っていることに触れました。 またPolymerが扱う新技術についても触れました。たくさんの新技術があり楽しいライブラリだと個人的には感じています。

    そしてPolymerを使ってMaterial DesignをWeb上で実現するところでは、実際にコーディング内容を紹介して具体的な使い方をイメージできるようにしました。 おおなんだか良く分かんないけど凄そうだ、と雰囲気だけでも伝わっていたら幸いです。

    最後のPaper Elementsの味見では、paper-rippleという波紋の広がるようなエフェクトについて扱いました。 ChromeのDevツールで中身を見てみたり、Githubで実際のソースコードが読めたりするあたりは興奮しますね!!(って自分だけかな...)。

    そんなこんなで30分で盛りだくさんにお話させて頂きました。その後の懇親会で「面白かったです」とたくさん言って頂き本当に嬉しかったです。 聞いて頂きまして本当にありがとうございました!!!

    また最後になりますが、本資料を事前に見て頂き様々なご指摘を頂きましたみなさま、ほんとうにありがとうございました。


    プレゼンについての余談

    最近話すために参加した4つのセミナーで、それぞれ「話すの上手いですねー」って言ってもらえました。 実はそれも目標にしていたので、本当に嬉しかったです! 「天性のものですかね」と言われたこともありましたが実は全くそんなことはありません。

    プレゼンで上手く話せるようになりたいという気持ちから、実はめっちゃ練習してます(誰にも見つからずにひっそりとw...)。 資料作成ではいきなりプレゼン資料を作るのではなくて、構成を作り、不足している点は調査や実験を行い、そして遅くても発表日の1週間前までに完成させます。

    その後は毎日のように1〜1.5時間くらい会議室にこもって、ひたすらプレゼン練習をしています。 最初の方なんて「あの〜」「え〜っと」などの連発で、所定時間に全く収まりませんw。 話す練習をしながら、資料の論理構成のおかしいところを直しながら、完成度を高めていきます。

    そして発表日の2日前くらいに、時間配分や内容やしゃべりで納得のいく状態にもって行きます(この状態にたどり着くまで本当に不安な気持ちでいっぱいですw)。 その後は指定時間内でいかに聴衆に楽しんでもらうか、どのようにプレゼン内で波を作るかなどレベルアップを図りつつ、当日を迎えます。
    この状態で当日を迎えられるとプレゼン直前での不安という気持ちはなくなり、逆にワクワクしてきます。「よーしこれから30分間のエンターテーメントだ」という感じのよくわからないテンションですw。

    プレゼンを上手くやりたいという思いから、毎回毎回でその時点での最高の準備をして当日に望むことで、次の時にはさらに1歩上に進めるように心がけています。
    Youtubeで見る様々なカンファレンスでは、とんでもなく素晴らしいプレゼンに多数出会います。そんな状態にまで自分もなれるようにこれからも頑張って行こうと思っています。

    これからも有益な情報をドンドンと発信していきたいと思っていますので、ぜひとも話せる機会をくださいー!!



    最後に

    今回の内容ではMaterial DesignとPolymerに触れました。これからのみなさまのWeb生活に少しでも役立てれば幸いです。

    今後も本ブログでは、フロントエンドに関する情報を書きたいと思います。気になった方はぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。
    最後までご覧頂きましてありがとうございました!



    [フロントエンド]Chrome39リリース内容まとめ。Generator、Web Animation Playback Control, Web Manifestなど

    $
    0
    0
    こんにちは@yoheiMuneです。
    10/9にThe Chromium BlogでChrome39ベータリリースが発表されました。今日はこのリリースでのChromeの変更点を書きたいと思います。

    画像

    Special Thanks to https://flic.kr/p/6MsR3




    目次




    Chromeベータ版の入手、新機能の動作設定

    Chromeのβ版は、以下から入手することが可能です。
    https://www.google.com/intl/en/chrome/browser/beta.html

    また、追加された機能によっては、chrome://flagsから「JavaScriptの試験運用機能を有効にする」を有効にする必要があります。



    ECMAScript6のGenerator

    ECMAScript6におけるジェネレーター(Generator)がサポートされました。 function*yieldという新しく定義されたものを利用して実装を行います。 ジェネレーターを用いることで、イテレーション処理について以下のような実装を行うことができます。
    // フィボナッチ数列を算出するジェネレーター
    var fibonacci = function*() {
        var pre = 0, cur = 1;
        for (;;) {
          var temp = pre;
          pre = cur;
          cur += temp;
          yield cur;
        }
    }
    
    // ジェネレーターから値を1件ずつ取り出す
    var f = new fibonacci();
    for (var n of f) {
        if (n > 1000)
            break;
        console.debug(n);
    }
    
    // またはジェネレーターのインスタンスはイテレーターなので、
    // next関数を使うこともできます
    var f = new fibonacci();
    var time = 10;
    while (time--) {
        console.debug(f.next().value);
    }
    
    上記のfor(;;)コードは一見無限ループを起こしそうですが、ジェネレーター(function*)の中にありyieldによって処理が停止するために、無限ループでプログラムが動作不能になることはありません。 JavaScriptの世界にはなかった仕組みなので、最初はとっかかりが難しいですね。



    Web Animation Playback Cntrol

    Chrome36から利用可能となったWeb Animationですが、今回のリリースでplay()pause()reverse()などのプレイバックコントロールが追加されました(Chromeの状態の確認はchromestatus.comが便利です)。Web AnimationはWeb上でアニメーションを実現するための統一的な手段を提供するものですが、例えば以下のような実装を行うことができます。
    // アニメーション作成
    var $elm = document.querySelector('h1');
    var player = $elm.animate([
      {transform: 'rotate(-45deg) scale(1)'},
      {transform: 'rotate(0) scale(2)'},
      {transform: 'rotate(45deg) scale(1)'},
    ], {
      duration: 1000,
      iterations: 3,
      easing: 'ease-in-out',
      direction: 'alternate',
      fill: 'both',
    });
    
    // プレイバックコントロール
    player.play();
    player.pause();
    player.reverse();
    
    またPolymerで作成された、Web Animationのプレイバックコントロールのデモも存在します。進化中のAPIって感じで楽しいですね。



    Web Application Manifest

    Android Chromeなどにおいて、スマホ本体のホームスクリーンにWebサイトのアイコンを置く場合に、アイコンのパスなどを<meta>タグや<link>タグを多用して指定する必要がありました。そしてこれらを表示するどのページにも設定する必要があり、これはコードの煩雑化やネットワークの浪費に繋がっていました。
    Chrome39からはWeb Application Manifestを用いることでそれらの重複した実装をmanifest.jsonなどのファイルにまとめることができ、その中に記載することで重複したコードを防ぐことができます。
    // HTML<link rel="manifest" href="manifest.json">
    // manifest.json
    {
      "name": "Web Application Manifest Sample",
      "icons": [
        {
          "src": "launcher-icon-1x.png",
          "sizes": "48x48",
          "type": "image/png",
          "density": "1.0"
        },
        {
          "src": "launcher-icon-2x.png",
          "sizes": "96x96",
          "type": "image/png",
          "density": "2.0"
        },
        // 解像度ごとの指定・・・
      ],
      "start_url": "index.html",
      "display": "standalone",
      "orientation": "landscape"
    }
    
    上記のように記載することで、「ホームに追加」の振る舞いを実装することができます。
    詳細はdeveloper.chrome.comを参照ください。<meta>タグなどの重複は、実務ではPHPなどの何らかのテンプレートファイルなどを用いて管理することが多いですが、Web上のネイティブ機能としても用意してもらえるのはありがたいですね。



    その他のアップデート

    上記以外にも、以下のようなアップデートが含まれています。


    Beacon API
    Chrome39からBeacon APIを用いて、Beaconの送信(ユーザー行動のトラッキングなどを目的にデータを送ること)をページ遷移などに関わらず行うことができました。 Beaconを送信したい場合には、例えば以下のように実装します。
    navigator.sendBeacon('http://localhost/log', 'Sent by a beacon!');
    より詳しい説明はhtml5rocksにて確認することができます。


    Scroll Offset
    高い解像度のデバイスに対応するために、スクロール位置(scrollTop、scrollLeft)は、小数点の値も含む精度の高い値を返すようになりました。


    XMLHttpRequestのProgressイベント内の変数名変更
    XMLHttpRequestのProgressイベント内において、loadedtotalのプロパティを用いるために、positiontotalSizeはduplicatedとなりました。



    最後に

    Chromium Dashboardを見ると試験運用としてService Workerの機能も追加されたようですね。Service Workerはオフラインを扱うための機能ですが内容がすごく濃いため、また別のブログで紹介したいと思います。

    Chromeのアップデートは楽しい機能がリリースされるので、毎回本当に楽しみにしています。今回は少し地味目なものも多かったですが、いつも通りたくさんの新機能が登場しましたね。Chromium Dashboardを見る限り今後のアップデートもとても期待しています。

    今後も本ブログでは、フロントエンドに関する情報を書きたいと思います。気になった方はぜひ、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。
    最後までご覧頂きましてありがとうございました!



    Viewing all 364 articles
    Browse latest View live