Code

2014年3月30日日曜日

日本はなぜ Front End を重視しないか?

 最近、あまり Yahoo! Japan 以外の Web Page をアクセスしてないけど、今日偶然ある不動産屋のホームページを見てみたら、遅くて、別のサイトを切り替えました。個人的には日本と欧米と比べると、Web 系の技術はまだまだ道が長いようです。ウェブサイトは Back End だけではないです。Front end が遅くなると、システム全体の評価が低くなります。
 Back End は自分のサーバーで動くから、遅いことはすぐ気づくでしょう。そこで、アルゴリズムの改善などして、インデックスを DB に作ったりして、まだなんとかなるでしょう。ただ、 Front End、いわゆる HTML 部分は顧客のシステムで動くので、いろんなブラウザがありますし、PC のスペックも様々です。最近、モバイルからのアクセスもかなり増えていますので、そこは通信や、JS の実行、ファイルのロードなどがさらに遅くなります。元々 Web Page が遅くなると、モバイルではさらに遅くなります。 Retina サイズのイメージを考えると、あ、頭が痛くなります。。。
 
 以前も書きましたけど、どうやって、ウェブサイトのロードスピードをあげるかと。
Web App が遅い??

 今日別の観点から、改善すべきところをリストします。
・まずは画像についてです。今のブラウザは画像をロードするにはいろんな加速技術を使っています。一つのドメインから、平行で二つの画像がダウンロードできますし、img の src を見つかったら、ウェブページをロードするスレッド以外、別スレッドでダウンロードするとか、いろいろありますけど、やはり、一つのリクエストのコストは大きいです。これを極力減らすべきです。
 一つの方法はすべての画像を一つにまとめて、CSS で background-position を使って、画像の一部分だけを表示します。これはスプライトといいます。下記のウェブページで画像をまとめて、一つのものにします。
 Sprit Generator
 後、もし画像のサイズがわかるなら、 img に width と height を書いて、ブラウザはそれを読んだら、まずポジションを確保します。そうすると、ページの内容がまず全部表示して、画像は後から少しずつ表示されます。全体にスピードアップような感じがします。
・次、CSS, JS ファイルを一つに、コンプレスすべきです。これは結構いろいろありますから、後日またツールを紹介します。
・後は JS についてですが、JS はいつも一つのスレッドで実行しますので、ブラウザがもし <script type="text/javascript">を見つかったら、他の処理を止めて、まずそこのコードを実行します。現在 V8とか、Gecko とかすごく速い JS Run Time Env があるが、やはり JS は遅いです。
 それで、すべての <script> を <body> の最後に置くと、DOM のロードなどが少し速くなります。ユーザは白い画面のままで待つ時間が短縮されます。
・CSS で作れるものは、画像を使わないほうがいいです。後、アニメーションとかも CSS を使ったほうがいいです。CSS はいろんなものが作れます:
  CSS GUI Icons
ブラウザはこれを実行するスピードがイメージをレンダーするスペードより、百倍速いです。
  CSS スタイルを適用するとき、別の文章があります。英語のものですけど。
  CSS Best Practice
  時間があったら、翻訳します。
・HTML の要素も減らしましょう。<div> など、必要なときだけ使うべきです。一つの画面に要素が 2000 個以上使うことはあまりないでしょう。複雑なウェブページ以外ですけど。後、後日また書きますが、JS にできるだけ HTML コードを書かないようにすべきです。メインテナンスでは、どうやって、そういう動作になったか読みづらくなります。

まぁ、ここまで一段落。それでは。

2014年3月27日木曜日

デフォルト値の重要性を見える例

 今後、Junior 開発者とコードレビューするとき、データ設計の重要性とデフォルト値を設定すべきだと伝える例です。

 すべての値を各レコードに書くより、あるデフォルト値を外に設定して、特別な時だけ、特別な値を設定すべき。
 これで、かなりのマニュアル操作が減らせます。
 一つの例:
 ある、JSON ファイルに uri があります。
    { sections: [
           {
               uri: "http://example.com/12345",
               story-uri: "http://example.com/{0}"
           },
           {
               uri: "http://example.com/45678",
               story-uri: "http://example.com/{0}"
           },
           {
               uri: "http://example.com/1804232",
               story-uri: "http://example.com/{0}"
           },
           {
               uri: "http://example.com/2431245",
               story-uri: "http://domain2.example.com/{0}"
           }
    ]}

 最後の一個だけ story-uri が他と違います。もし、こういうレコードが何十個にあると、修正するにはかなり時間がかかります。

それで、下記の JSON に変更すれば:
    { sections: [
           {
               uri: "http://example.com/12345"
           },
           {
               uri: "http://example.com/45678"
           },
           {
               uri: "http://example.com/1804232"
           },
           {
               uri: "http://example.com/2431245",
               story-uri: "http://domain2.example.com/{0}"
           }
     ],
     common-story-uri: "http://example.com/{0}"
}

 コードには、まず各レコードに story-uri があるかどうかチェックして、もしあったら、それを使います。もしなければ、common-story-uri を使います。多分5行ぐらいのコードですむことです。こうすれば、不要な重複をなくして、レコードの story-uri を変更するときも一目瞭然です。

 これは Fall back というんです。それでは。

愚痴:Android の WebView が遅い。。。アジャイルいろいろ => やはりヒーローが必要です。

tl;dr : 基本的に Android の WebView は遅いです。特にアニメーションがカクカクする場合があります。JS でのアニメーションは使わないようが良いでしょう。また AngularJS を使うと、また一層遅くなるので、もしパフォーマンスに気にするなら、React.js を使ってみてください。
 速くする方法としては、画面を pre-load するだけです。ユーザーの動作を考慮して、Idle 時間に先に作っておいて、表示するとき、画面を切り替えだけ行います。
 
 愚痴です。いい内容、答えもないので、ただメモして、いろいろ考えたいだけです。時間の大切な方はご遠慮。

 最近 Android バージョンのアプリが作り始まりました。あまり進捗など見てないけど、Front End チームの一員として、会議に呼ばれました。問題は一つ、iOS に比べると、Android の WebView がちょっと遅くて、スライドする時、画面のレンダリングが追いつけない。。。 これじゃ、また何所が遅いかをみんなで討論して、何か解決方法がないかと。
 実際見てみたら、そんなには遅くないけど、動きが速いとき、やはり iOS には及ばない。。。これは Android 自身の問題じゃないかと。みんな知っていることでしょうと。が、そうは行かないですけどね。
 今は独自の jQuery Light のライブラリを実装して、すべてのページに使っているし、かなり細かくファイルを分けて、ロードの時間をできるだけ短縮しました。実際一つだけのページをロードすると、速いとは言わないが、遅くないと思います。では、問題は何所にあるだろう?
 今の iOS App は表示している画面以外、別のスレッドで次の画面と前の画面をレンダリングしています。なぜなら、Google Analytics によると、ユーザが一つの画面に止まる時間は12秒でもある。コンピュターの世界にいると、1秒ではかなりの時間です。その時間を使って、裏でいろいろマルチスレッドがやります。
 Android アプリはあまりこういう工夫はしていないそうです。
 まぁ、あまり言いたくないけど、何か問題を発見したとき、会議にする前に、まずみんなに聞いてみましょうよ。もちろん知っている人は知らない人はいるんですけどね。これはまたアジャイルの弱点は少し出て来た。
 今のプロジェクトでは、設計ドキュメントもないし、仕様書もない。。。すべてがソースコードにあります。これはすばらしいことです。ただ、問題はみんなソースコードを書くとき、開発者のレベルより、コメントが読めたり、読めなかったりします。後、ソースコードを読みたくない開発者もいるから、すべての知識を浸透するには難しいです。
 それでエンジニアリードという職をもうけて、どう設計するかの指導役ですが。今、その方はあまり iOS, Android アプリの経験がないので、いろいろ難しいことがあります。じゃ、今まで何をやって、いい結果になったか問う言うと、チームにヒーローがいるからです。
 ヒーローがすべて知っているし、指導もできるし、自ら難しい問題を解決してくれます。今のチームにはそういう人がいなければ、どうなっているかは本当にわからない。この状況を変えるにはどうすればいいでしょうかね。。。
 それでは。
 

2014年3月22日土曜日

Mac OS X で JDK の切り替え

 最近、隣のお姉ちゃんが間違って JDK 1.8 をインストールしてしまいました。うちのプロジェクトは JDK 1.7 を使っていますけど。。。
 まず変なエラーが出て: Saxon-HE-9.4.jar のSaxon he 9.4 XPathfactory#newInstance() の Configure ファイルのなんちゃらがフォーマットが正しくない。。って。何でだろうと思ったんですけど、見たことのないエラーだし、どうしようかなと。
 Tomcat の設定、Eclipse の設定、Maven ファイルのライブラリのバージョンをいろいろ調べましたが、変なものが見当たらない。。。
 それで、正しく動いている War ファイルをサーバーからダウンロードして、入れてみたら、やはり同じエラーが出て来ました。まぁ、これで、ローカルマシンの問題だと断定しました。JAVA_HOME を見たら、1.8 となっていました。。。
 これは、これは。すかさず、1.7 をダウンロードしました。ちなみに Oracle のウェブサイトからは変なファイルがダウンロードしたので、インストールできませんでした。以前保存した dmg ファイルをまた Air Drop を使って、運んで、無事インストールが成功しました。
 じゃ、次は JDK バージョンの切り替えです。いろんな方法がありますけど、一番いい方法を見つかったので、メモしておこう。
 まず、Java のバージョンを調べる /usr/libexec/java_home というコマンドがあります。
それを使って、.bashrc, .bash_profile などのファイルに
export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)
を書けば、そのバージョンの JDK が使えます。
もっと便利に、コマンドの追加して
http://superuser.com/questions/490425/how-do-i-switch-between-java-7-and-java-6-on-os-x-10-8-2

alias java_ls='/usr/libexec/java_home -V 2>&1 | grep -E "\d.\d.\d_\d\d" | cut -d , -f 1 | colrm 1 4 | grep -v Home'

function java_use() {
    export JAVA_HOME=$(/usr/libexec/java_home -v $1)
    export PATH=$JAVA_HOME/bin:$PATH
    java -version
}
切り替えも簡単になります。
まぁ、難しいことではないけど、間違ってインストールすると、変なエラーが出たりしますので、今後自分も注意を。
それでは。

2014年3月19日水曜日

iOS ファイル HTTP 経由ダウンロード、失敗することがある

 この時代になると、HTTP サーバーを接続して、ファイルをダウンロードをするには、かなり普通なことになりまして、サーバーがちゃんと生きてあれば、通常は失敗しないだろうと。3G でも、4G でもそうですが。
 実際では、失敗することがあります!えっ、えっ?
 回数が足りないだけです。:)
 通常 3G で接続するとき、みんなかなり注意しますけど、Wifi で接続する場合、必ず成功するのはないです。
 それで必ず、Timeout や、失敗するかどうかをチェックしましょう。
 先日ちょっとひどい目に遭いました。。。
 いくら発生率が低くても、発生することがあり得ます。Shit happens. :)

モバイル Web App が遅い ??

Update: iOS 8 から UIWebView のウップグレードとして、WKWebView が追加されました。Nitro も使えるようになりましたので、in App WebView がさらに速くなりました。

最近面白い記事を二つ読みました。一つはなぜ Mobile Web App が遅いか
        why mobile web apps are slow
 もうひとつは遅くても、ちゃんと開発すれば、全然感じられないほど速い。
        the making of fastbook an html5 love story

 ネーテブの App と比べると、Web App が遅いのは当然、ただ何所まで遅くなるかはみんなわからない。それで、いろんな文章が出て来た。読んだ文章は結構データも充実しているので、信頼できるでしょう。ただ、ネーテブコードでも遅くなるケースも結構あるので、これは開発者次第です。
 そうすると、Facebook App をSencha を使って、開発すると、ネーテブとほぼ同じ、またはもっと速いよとの文章です。
 実際コードを読んでみると、HTML5 は遅いかもしれませんが、まさしく Sencha のように、よく作ると、あまり感じられません。
 じゃ、Web App を作るとき、何を注意すべきでしょう:
・まず、DOM 操作が遅いから、できるだけ減らしましょう。DOM 操作というのは HTML Element を作成して、その後、append, before などの関数でそれを挿入すること。DOM ツリーが更新されるので、時間がかかります。特に、IE7, IE8 の時代、JavaScript 自体が遅いし、大量のノードを挿入すると、メインプロセスが止まって、無応答になります。
・できるだけ、文字列で HTML 文を作って、一気にDOM にいれましょう。これは .innerHtml を使います。これは Mustache や、 Handlebars と言ったテンプレートライブラリの用途です。もし、小さいものならば、 underscore.js も template 関数があるので、それを使ってもいいでしょう。
・resize, scroll などにバンドルするハンドラ関数を throttle や debounce 関数を使って、実行の回数を減らしましょう。scroll すると、一瞬にかなりの数のハンドラ関数が呼ばれますので、かなり負荷が高いです。ある時間を経過後、ハンドラを呼べばいいでしょう。
・setTimeout を有効に使いましょう。setTimeout(func, 0) の意味は JavaScript のコールスタックが空になったら、 func を実行するのです。元々注意すべきところは setTimeout は何時実行されるかは不明ですので、ある程度の時間を経過したらというのです。例えば、いっぱい HTML 文字列を挿入して、挿入後、 Focus を新しく生成した Input に設定する処理では、何時挿入が完成するかはわからないので、その後で setTimeout 0を設定すると、挿入完了したら、実行されます。
・DOM の Event Propagation を有効に利用しましょう。例えば、li タグ中の div にハンドラ関数をバインドしたい場合、li の親の ul にバインドすれば、event.target でクリックした要素が取得できますので、一つのハンドラですむのです。li にいっぱいハンドラ関数をバインドすると、結構コストがあります。
・ロードするライブラリを注意しましょう。いつどのようなライブラリがロードされるかちゃんと管理しましょう。本来なら require.js がこういう仕事のためですが、自分はそのライブラリちょっと気に入らないので、使いたくないです。ライブラリをロードするにはかなりコストがありますので、最小限のコードを実行しましょう。例えば zeptojs を使ったりしてしましょう。まぁ、本当に速くしたいなら、自分で自分用のライブラリを作りましょう。そうすると、エラー処理とか省略できるので、かなり速くなります。自作の場合、jsperf.com がかなり良くできています。使ってみてください。
・写真のロードや、ファイルのサイズ、後キャッシングするかどうかもかなり影響しますので、注意しましょう。
 - まず写真について。現在のブラウザは写真をロードするには複数のスレッドを使っていますので、二つのドメインからロードすればいいです。そうすると、Static のリソースは別々のドメインに保存すればいいです。
 - 次ぎ、ファイルのサイズについて。ファイルの請求はかなり時間がかかりますので、jsや、cssファイルを一つのファイルにまとめて、不要なスペース、改行を全部削除しましょう。 Ruby や、Node.js にいろんなツールがあるから、有効に使いましょう。RoR の Asset Pipeline はかなり良くできてますので、使えるなら、使いましょう。
 - キャッシングについて。もし一つのファイルは変更する予定がないなら、expire 日付を一年以降に設定したりして、ロードは最初の一回のみでいいでしょう。RoR ではファイル名の後に SH 列があるので、内容が変更したら、その文字列も変わるので、かなり便利です。
・HTML5 の新しいフェーチャーを使いましょう。
 - Local Storage を使いましょう。Cookie が送信されるので、サイズを累計すると、かなり大きくなります。だから、必要なときだけ、データをロードして、送信しましょう。
 - Offline 機能を有効に使いましょう。これはキャッシングと同じような感じですが、 Offline 機能を使うと、どのファイルをどう更新するかは自分でコントロールできます。
 - 動画なら、CSS を使いましょう。JS の fadeIn, fadeOut 関数などはかなりCPU 使いますので、良くないです。 CSS だと、GPU を使って、画像が作られますから、ブラウザの負担た小さいです。
・ CSS についても注意しましょう。できるだけ、div .cls1 .cls2 {} のようなものを書かないでください。これはブラウザが CSS を適用する際に、.cls2 から、要素すべての親をスキャンして、div .cls1 となっているかどうかチェックするから、時間がかかります。できれば、.cls2 だけ書いてください。これは速くなります。

まぁ、他にもいろいろありますけど、後日また追加します。
それでは。

2014年3月6日木曜日

Chrome の Same Origin Policy を無効にする

Windows の場合を補足しておこう:
C:\Users\%USERNAME%\AppData\Local\Google\Chrome\Application\chrome.exe --disable-web-security
# つまり、chrome.exe の後ろに --disable-web-security を追加する。またはショットカットを作って、パラメターとしてそれを渡す。

ただし、通常の chrome を実行している間、同じプログラムで二つプロセスが実行できないため、エラーが出ます。
こういうとき、下記のコマンドを実行しよう。
chrome.exe --user-data-dir="C:/Chrome dev session" --disable-web-security
これで、違う chrome プロセスが実行されますので、安全モードの Chrome と web security 無効の Chrome が同時に実行できます。

Mac で一番いい方法見つけたので、メモしておこう。
.profile に下記を追加して、コマンドラインで chromex を実行すれば、 別ドメインへの XHR が実行できるようになる。

chrome () {
  /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome $* 2>&1 &
}

alias chromex="chrome --disable-web-security"

 Safari の iOS web inspector はちょっと使いにくい。。。
 Mobile での HTML5 widget 開発には役立つだろう。:)

 それでは。