ハイパフォーマンスWebサイト

G.W.も後半。ちょっとは勉強しておこう。ということで今読んでいる本はこれ。

ハイパフォーマンスWebサイト ―高速サイトを実現する14のルール

ハイパフォーマンスWebサイト ―高速サイトを実現する14のルール


パフォーマンスチューニングというと何かとサーバーサイドでやることが多い。ビジネスロジックの見直しやDBなど。しかし本書はフロントでのパフォーマンスチューニングのテクニックについて述べているところが新しい。本書では14のテクニックが紹介されています。

  1. HTTPリクエストを減らす
  2. CDNを使う
  3. Expiresヘッダを設定する
  4. コンポーネントgzipする
  5. スタイルシートは先頭に置く
  6. スクリプトは最後に置く
  7. CSS expressionの使用を控える
  8. javascriptcssは外部ファイル化する
  9. DNSルックアップを減らす
  10. javascriptを縮小化する
  11. リダイレクトを避ける
  12. スクリプトを重複させない
  13. ETagの設定を変更する
  14. Ajaxをキャッシュ可能にする

どうでしょう。タイトルを見ただけで、読みたくならないですか。例えばスタイルシートは先頭に置いてスクリプトは最後に置くのはなんでだろう?とか。おそらく会社でもここまで指摘してくれる先輩というのはいないのではないでしょうか。いなけりゃ本で勉強して自分がなればいいのだ。ハハ。値段も1800円と技術書としては非常に安い!一家に一冊!

まだまだ読み始めたばっかりだけど、早速「HTTPリクエストを減らす」の章で紹介されていたCSSスプライトなる技を覚えた!これ結構使われているテクニックなんですね。多分知らない方が恥ずかしいんだろうけど・・・。例えばGoogle。つい最近トップページにGMailYouTubeのアイコンが表示されるようになりました。これがCSSスプライトで実現されています。
例えば「おすすめ」タブには5つのアイコンが表示されていますが、それぞれのアイコン画像を5つ指定しているわけではありません。5つ指定すると5回のHTTPリクエストが必要ですが、HTTPリクエストの回数を減らすためにアイコンが連なった画像を1回だけ取得し、CSSで、1つだけ表示されるようにしています。その連なった画像は以下URLで確認できます。
http://www.google.co.jp/intl/ja/images/jawh_prodiconl3.png


連なった画像からどのように任意の1つを取り出すのか、というのはgoogleのページのソースを読めばすぐ分かります。分かってしまえば、いつも通りふーんって感じです。以下はそれをちょこっとだけいじってスライドさせてみました。実行してもらえば、CSSスプライトの雰囲気が分かってもらえるのではないでしょうか。

<html>
<head>
<style type="text/css">
.iconl {
  height:35px;
  overflow:hidden;
  position:relative;
  width:35px;
}
</style>
</head>
<body>
<div class="iconl" style="border:solid 1px black;">
  <img id="icon" src="http://www.google.co.jp/intl/ja/images/jawh_prodiconl3.png" style="position:absolute; left: 0px;">
</div>

<script type="text/javascript">
(function(){
  var elem = document.getElementById("icon");
  var counter = 0;

  var callback = function() {
    if ( counter < -560 ) return;

    elem.style.left = --counter;
    setTimeout(callback, 0);
  };

  setTimeout(callback, 0);
})();
</script>

</body>
</html>

javascriptにおける正規表現テクニックのまとめ

正規表現による置換において変数を利用する方法

例えば以下のような文字列の最後についている余計な区切り文字「,」を除去する正規表現を考える場合、どのサイトを見ても以下のようにやる方法は載っている。

'1,2,3,4,5,'.replace(/,$/, '');

しかし、区切り文字が時と場合によって異なる場合、例えば変数delimiterに格納されて渡ってきた場合、これを利用してパターンを組む場合、以下のようにしてはだめ。「/」で囲まれるのはパターンなので、「pattern」は変数として扱われるのではなく、「pattern」という正規表現パターンとして扱われる。

var delimiter = ',';
var pattern = delimiter + '$';
'1,2,3,4,5,'.replace(/pattern/, '');

ではどうすればよいのか。replaceメソッドの第一引数は正規表現オブジェクトを取ることもできるので、それを利用する。

var delimiter = ',';
'1,2,3,4,5,'.replace(new RegExp(delimiter + '$'), '');

オプションを伴う場合

区切り文字「,」を空白ですべて置き換えたい場合、以下のようにgオプションを追加することで、いわゆるreplaceAllを実現するのだが、

'1,2,3,4,5'.replace(/,/g, ' ');

これをRegExpを使って書く場合は次のようにする。

var delimiter = ',';
'1,2,3,4,5'.replace(new RegExp(delimiter, 'g'), ' ');

パターンマッチングした部分の抽出

例えば「2008年4月22日」を「2008/4/22」とするにはどうすればいいだろうか。以下のように正規表現パターンにおいて「()」で囲むことでマッチング部分を$変数で利用できる。

'2008年4月22日'.replace(/(\d{4})年(\d{1,2})月(\d{1,2})日/, '$1/$2/$3');

では、「the jacket and the bag are mine.」という文章を「the JACKET and the BAG are mine.」という文章に変更する場合はどうだろうか。同じように抽出する方法で考えると次のようになる。

'the jacket and the bag are mine.'.replace(/the\s(\w+)\s/g, 'the $1.toUpperCase() ');

しかしこれはうまくいかない。$1に対してtoUpperCaseメソッドは適用されず、「the jacket.toUpperCase() and the bag.toUpperCase() are mine.」とそのまま表示されてしまう。ではどうするのか。

replaceメソッドの第二引数は関数も渡せる

replaceメソッドの第二引数に関数が渡せるので、それを利用する。

var upper = function(a, b) {
  return 'the ' + b.toUpperCase() + ' ';
}

'the jacket and the bag are mine.'.replace(/the\s(\w+)\s/g, upper);

ちなみに第一引数aはパターンマッチングした「the jacket 」で、第二引数bはさらに「()」で抽出された「jacket」となる。


これだけ知っていれば正規表現を使っていろいろ出来ると思う。

javascriptの実行順序

知っている人にとっては当たり前すぎることだと思いますが、私自身整理できていなかったのでちょっとまとめる。
次のようなhtmlがあった場合、画面の表示およびjavascriptのアラートの表示のタイミングはどうなるだろうか。
ちなみに$(function(){alert('$関数中');});はjQueryの関数で、DOMのロードが終わったタイミングで呼び出される。

<html>
<head>
<script type="text/javascript" src="jquery-1.2.3.js"></script>
<script type="text/javascript">

$(function(){alert('■$関数中');});
alert('■javascriptタグ中1');
window.onload = function(){alert('■window onload');};

</script>
</head>
<body onload="alert('■body onload');">
  <p>hello!</p>

<script type="text/javascript">
  alert('■javascriptタグ中2');
</script>

  <p>world!</p>

  <img src="sample.png">
</body>

<script type="text/javascript">
  alert('■javascriptタグ中3');
</script>

</html>

正解は以下のようになる。firefoxで動作確認。

  1. 「■javascriptタグ中1」表示
  2. 画面に「hello!」と表示
  3. 「■javascriptタグ中2」表示
  4. 画面に「world!」と画像が表示(ただし画像といっても実際の画像はまだ表示されていない。枠だけ)
  5. 「■javascriptタグ中3」表示
  6. 「■$関数中」表示
  7. 画面に実際の画像表示
  8. 「■body onload」表示

ポイントは、画像が遅れて表示されるところと、window.onloadとbodyのonloadの両方定義しておくとbodyのonloadが勝つといったところでしょうか。ちなみに画像ですが、2回目以降はキャッシュされるので、world!が表示されたタイミングで表示されています。
この辺のタイミングを知ってないと、javascriptでDOM操作するときに、あれ?取れない!ってことになったりするので。

function

javascriptやってて何かと記述することの多い「function」という単語。functionをいかに早く打つかというサイトがあったw
http://shohoji.net/labs/function/


とりあえず0.499秒をはじきだして、現時点で今週の2位にランクイン♪けど、1位の木村雄一郎さんの記録0.022秒ってどゆこと!?

世界の言語でこんにちは

Google AJAX Language APIを使ってもういっちょ。「hello」をGoogle AJAX Language APIで変換できる言語の数だけやってみた。
本当は「CHINESE : 你好」のように表示したかったんだけど、いかんせん非同期なもんで、show関数を一通り実行してからgoogle.lnaguage.translate関数の結果が返るため、以下の様ないけてない表示になります。。
非同期だからというのは嘘。変数スコープの問題でした。2009.2.15追記

CHINESE
CHINESE_SIMPLIFIED
CHINESE_TRADITIONAL
ARABIC
FRENCH
GERMAN
ITALIAN
JAPANESE
KOREAN
PORTUGUESE
RUSSIAN
SPANISH
DUTCH
你好
你好
你好
&#1605;&#1585;&#1581;&#1576;&#1575;
Bonjour
Hallo
Ciao
こんにちは
&#50504;&#45397;&#54616;&#49901;&#45768;&#44620;
Olá
Здравствуйте
Hola
Hallo

あらら。アラビア語とハングル語がうまく表示されて無い・・・
修正したプログラムの結果は次のようになります。いつの間にやらすごい数になっていますね。言語は用意されているけど、まだ翻訳が全部できるわけではなさそう。中国語が前回と違う結果に・・2009.2.15追記

AFRIKAANS :
ALBANIAN : përshëndetje
AMHARIC :
ARABIC : &#1605;&#1585;&#1581;&#1576;&#1575;
ARMENIAN :
AZERBAIJANI :
BASQUE :
BELARUSIAN :
BENGALI :
BIHARI :
BULGARIAN : привет
BURMESE :
CATALAN : hola
CHEROKEE :
CHINESE : 喂
CHINESE_SIMPLIFIED : 喂
CHINESE_TRADITIONAL : 餵
CROATIAN : zdravo
CZECH : ahoj
DANISH : hej
DHIVEHI :
DUTCH : hallo
ESPERANTO :
ESTONIAN : tere
FILIPINO : hello
FINNISH : hei
FRENCH : bonjour
GALICIAN : Ola
GEORGIAN :
GERMAN : hallo
GREEK : γεια σου
GUARANI :
GUJARATI :
HEBREW : &#1513;&#1500;&#1493;&#1501;
HINDI : &#2361;&#2376;&#2354;&#2379;
HUNGARIAN : helló
ICELANDIC :
INDONESIAN : halo
INUKTITUT :
ITALIAN : ciao
JAPANESE : こんにちは
KANNADA :
KAZAKH :
KHMER :
KOREAN : &#50504;&#45397;&#54616;&#49464;&#50836;
KURDISH :
KYRGYZ :
LAOTHIAN :
LATVIAN : sveiki
LITHUANIAN : labas
MACEDONIAN :
MALAY :
MALAYALAM :
MALTESE : hello
MARATHI :
MONGOLIAN :
NEPALI :
NORWEGIAN : hallo
ORIYA :
PASHTO :
PERSIAN :
POLISH : witaj
PORTUGUESE : Olá
PUNJABI :
ROMANIAN : salut
RUSSIAN : привет
SANSKRIT :
SERBIAN : здраво
SINDHI :
SINHALESE :
SLOVAK : ahoj
SLOVENIAN : zdravo
SPANISH : hola
SWAHILI :
SWEDISH : hej
TAJIK :
TAMIL :
TAGALOG : hello
TELUGU :
THAI : &#3626;&#3623;&#3633;&#3626;&#3604;&#3637;
TIBETAN :
TURKISH : merhaba
UKRAINIAN : привет
URDU :
UZBEK :
UIGHUR :
VIETNAMESE : xin chào

続いてソースです。
google.language.Languagesで登録されている言語分for-in文でまわしているところがポイントです。

<html>
  <head>
    <meta http-equiv="content-type" content="text/html;charset=UTF-8"></meta>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load("language", "1");

      function show() {
        var r = document.getElementById('r');

        for (var lang in google.language.Languages) {
          if(lang == "ENGLISH" || lang == "UNKNOWN") continue;

          r.innerHTML += lang + '<br>'

          google.language.translate("hello", "en", google.language.Languages[lang], function(result){
            r.innerHTML += result.translation + '<br>';
          });
        }
      }

      google.setOnLoadCallback(show);
    </script>
  </head>
  <body>
      <div id="r"></div>
  </body>
</html>

修正したプログラムは以下です。2009.2.15追記

<html>
  <head>
    <meta http-equiv="content-type" content="text/html;charset=UTF-8"></meta>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load("language", "1");
      google.setOnLoadCallback(function(){
        var showResult = document.getElementById('r');
        for (var lang in google.language.Languages) {
          if(lang == "ENGLISH" || lang == "UNKNOWN") continue;

          (function(l){
            google.language.translate("hello", "en", google.language.Languages[l], function(result){
              showResult.innerHTML += l + " : " + result.translation + '<br>';
            });
          })(lang);
        }
      });
    </script>
  </head>
  <body>
      <div id="r"></div>
  </body>
</html>

変数スコープを解決するために無名関数で囲んでいるところがポイントです!2009.2.15追記

Excite翻訳が簡単に作れた

■googleからの贈り物
ここで紹介されているように、googleからGoogle AJAX Language APIなるものがリリースされたようです。
ということで、勉強がてらExcite翻訳似のサイトを作ってみました。

ちなみに本物のExcite翻訳では以下のようになります。どちらの英語がより正しいのかは判断できませんが、個人的にはExciteの英語の方が読みやすいです。

以下はソース。

<html>
  <head>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
    function $(id) {
      return document.getElementById(id);
    }

    var lang = {en_to_ja:["en", "ja"], ja_to_en:["ja", "en"]};

    google.load("language", "1");

    function translate() {
      var s = $("l").options[$("l").selectedIndex].value;
      google.language.translate($("source").value, lang[s][0], lang[s][1], function(result) {
        $("result").value = result.translation;
      });
    }
    </script>
  </head>
  <body>
    <div style="margin-left: auto; margin-right: auto; width: 750px;">
      <div style="width: 325px; height: 315px; background: #a0b8c8; float:left; padding-left: 4px; padding-top: 4px;">
        <textarea id="source" rows="15" cols="36"></textarea>
      </div>
      <div style="float: left; width: 70px; margin: 10px;">
        <select multiple="multiple" style="margin-top: 50px;" id="l">
          <option value="en_to_ja" selected="">英→日</option>
          <option value="ja_to_en">日→英</option>
        </select>
        <div style="background: #ff9900; width: 50px; height:25px; margin-top: 20px;">
          <button onclick="translate();" style="margin-top: 1px; margin-left:1px;">翻訳</button>
        </div>
      </div>
      <div style="width: 325px; height: 315px; background: #a0b8c8; float:left; padding-left: 4px; padding-top: 4px;">
        <textarea id="result" rows="15" cols="36"></textarea>
      </div>
    </div>
  </body>
</html>

文字列で渡ってきた日付情報をフォーマットする(再)

以前のエントリ:文字列で渡ってきた日付情報をフォーマットする


以前書いたjavascript正規表現の例ですが、別のやり方がわかったので追記します。

var s = '2008年3月2日 22時36分10秒';
var x = s.replace(/(\d{4})年(\d{1,2})月(\d{1,2})日\s+(\d{1,2})時(\d{1,2})分(\d{1,2})秒/, '$1/$2/$3 $4:$5:$6');
console.log(x); // 2008/3/2 22:36:10

これなら1発♪
ポイントは、マッチした箇所を$1や$2等で抜き出して使っているところ。抜き出したい箇所はカッコで囲むといい。例えば2008を抜き出したければ「(\d{4})年」とする。これは「2008年」にマッチするわけですが、カッコは「年」を除いた箇所にかかっているので、「$1」は「2008」となります。