ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

2014年5月6日火曜日

超チューニング祭に参加してきた

2014/04/26(土) - 04/27(日)に開催されたニコニコ超会議3の、超チューニング祭に参加してきました。

SP版ニコニコ動画のトップページをチョッパヤにしてくれ、という課題に友人と二人で二日間かけて取り組んできました。

特に賞には引っかからなかったのですが、せっかく参加したので今回行った高速化の施策を軽く紹介しようと思います。

  1. jQueryからZepto.jsに変更
  2. JavaScriptの結合・圧縮
  3. 画像をCSSスプライトにまとめる
  4. 画像を減色
  5. HTMLの書き直しと圧縮
  6. CSSの書き直しと圧縮
  7. ファーストビューより下の画像を遅延読み込み

1. jQueryからZepto.jsに変更

共通ライブラリとして読み込まれていたjQueryをZepto.jsに変更しました。
これだけで、68KBくらい削減できます

2. JavaScriptの結合・圧縮

トップページだけで10個ものJavaScriptファイルが読み込まれていたので、uglify.jsを利用して1ファイルに結合・圧縮してしまいました。
また、アカウント設定なしでGoogle Analyticsのurchin.jsが読み込まれていたので削除してしまいました(なお本番のトップページでもアカウント設定なしで読み込まれている模様)。そもそもurchin.jsとかかなり古いコードなのでやめてほしいですね…

3. 画像をCSSスプライトにまとめる

サムネイルとか動的に切り替わる画像以外はCSSスプライトにまとめると、リクエスト数が減っていい感じです。
画像のスプライト化には自作のGruntタスクであるgrunt-spritesheet-generatorを利用しました。

4. 画像を減色

imageoptimとimagealphaを使って画像を減色します。特にpng画像はimagealphaを使うことで60-70%ほどサイズを削減できます。

5. HTMLの書き直しと圧縮

UIの改善のための書き直しと併せて、タグのネストを浅くしたりしました。
その上でgrunt-contrib-htmlminを使いコメントや改行コード、不要なスペースを削除しました。

6. CSSの書き直しと圧縮

grunt-cssoを使ってcssの圧縮をしました。cssoを利用すると、コメントや空行の削除だけでなくcssの構造的な最適化まですることができます。
また、SASSに書き直して利便性を上げるとともに未使用のCSSプロパティを消しています。

7. ファーストビューより下の画像を遅延読み込み

CSSスプライトにまとめられない画像(バナーや動画のサムネイルなど)は、ユーザーのスクロールに合わせて遅延読み込みされるようにしています。こうするとファーストビューより下の画像のリクエストを遅らせることができます。表示領域に入らなかった画像はリクエストされることもありません。この機能の実装にはlazyloadというライブラリを使っています。


以上です。

js/css/画像をすべてインラインで書いてしまいリクエストを一つにする、とか思いもよらない(でも言われてみれば合理的)な施策があったり、なかなか勉強になりました。(個人的には画像をローカルストレージにキャッシュするのが面白かったです)

他の人と同じ課題をもらって実装を競うとかしたことのない経験だったのでなかなか面白かったです。
次こんな機会があったらLTもしてみたいですね。

※一応実際のコードもおいておきます

参考(他の参加者の記事):
- 本の虫: 超会議3の超チューニング祭の感想
- レポート - 超チューニング祭で努力賞(最速賞)をとるためにやったこと - Qiita
- ニコ動 超チューニング祭で最優秀賞もらいました
- 超チューニング祭に参加した - masarakki's blog
- JavaScript - 超チューニング祭に参加&表彰した - Qiita
- kmizu/slide_cho_tuning
- 超会議の偏った感想 - mizchi's blog

2013年7月16日火曜日

jsrender 1.0preから1.0betaにバージョンアップした時に躓いたところ

javascriptテンプレートエンジンの一つであるJsRender
今日久しぶりにレポジトリを確認したら1.0betaにバージョンアップしてました。
今まで使っていたのが1.0pre。ただ jsrender.js を更新するだけでは使えず、コード側にもちょっと更新作業が必要でした。

ヘルパー

1.0preでは jsviews.helpers がヘルパー登録用メソッドだったのですが、1.0betaでは jsviews.views.helpers に移動しています。

// 1.0pre ----------
jsviews.helpers({
  fooHelper: function() {},
  barHelper: function() {}
});

// 1.0beta ----------
jsviews.views.helpers({
  fooHelper: function() {},
  barHelper: function() {}
});

カスタムタグ

1.0preではヘルパーと同様に登録用メソッドが jsviews.tags から jsviews.views.tags に移動しています。
また、1.0preではカスタムタグの描画メソッドも this.renderContent から this.tagCtx.render に変わっています。

// 1.0pre ----------
jsviews.tags({
  fooTag: function(param) {
    var ret = '';
    // カスタムタグのロジック…

    ret = this.renderContent(param);

    return ret;
  },
  barTag: functioN() {}
});

// 1.0beta ----------
jsviews.views.tags({
  fooTag: function(param) {
    var ret = '';    
    // カスタムタグのロジック…

    ret = this.tagCtx.render(param);

    return ret;
  },
  barTag: function() {}
});

とりあえず現時点で対応したのは上記の点のみ。
別件ですがjsrender.min.jsにバージョン番号つけてほしい…(jsrender-1.0beta.min.jsみたいに)

2013年5月27日月曜日

ドット記法のStringを元にオブジェクトのメンバを取得する[JavaScript]

以下のような階層の深いオブジェクトから、任意の階層にあるプロパティを楽に取得したくて作ってみました。

var foo = {
  bar: {
    hoge: 1,
    fuga: {
      isBar: true
    }
  }
};

こんな感じ。

var findRecursively = function(keyString, src) {
    var varSplitter = /\./,
        srcObj = src || window,
        keyAry = keyString.split(varSplitter),
        key;

    while (key = keyAry.shift()) {
      obj = obj[key];
      if (!obj) {
        return null;
      }
    }
    return obj;
};

var result = findRecursively('foo.bar.hoge');
console.log(result);  // 1

var result2 = findRecursively('bar.fuga.isBar', foo);
console.log(result2); // true

第一引数には、取得したいプロパティの場所をドット記法で表記したStringを渡します。
第二引数には第一引数で渡したプロパティを探したいオブジェクトを渡します。
第二引数がない場合はwindow変数から探します。

if ('foo' in window && 'bar' in foo && …) {
  ...
}

みたいな書き方をしないで済むようになります。

2013年2月11日月曜日

Frontrend vol.4に参加してきた

2013年2月10日日曜日

[JavaScript]任意の数の引数を受け取り処理を行う

2013年2月3日日曜日

Markdownで投稿した記事のソースコードの見た目をいい感じにする

2013年1月31日木曜日

やったーBloggerにmarkdownで記事を投稿できたよー\(^o^)/

2012年11月27日火曜日

[JavaScript]Objectをループしたい時はObject.keys()が便利

Objectをループで回す時はfor ~ inを使うのが定番だけど、そのままだとオブジェクトのプロトタイプをさかのぼってすべてのプロパティを見てしまうのでちょっとした工夫が必要になる。
こんな感じ。
var hoge = {
  a: 'paramA',
  b: 'paramB'
};
for (var key in hoge) {
  if (hoge.hasOwnProperty(key)){
    console.log(key + 'is property of hoge!');
  }
}

これだとループの中で毎回hoge.hasOwnProperty()が呼び出されるので、ちょっと遅くなってしまう。
そこで、Object.keys()の出番である。
Object.keys()は引数に渡したオブジェクトのプロパティだけを配列で返してくれる。
使い方はこんな感じ。

var hoge = {
  a: 'paramA',
  b: 'paramB'
};
var hogeKeys = Object.keys(hoge);

for (var i = 0, len = hogeKeys.length; i < len; i++) {
    console.log(hogeKeys[i] + 'is property of hoge!');
}

この方法だとループの中でhoge.hasOwnProperty()を呼ばなくていいからちょっと早い。

2012年7月12日木曜日

第31回 HTML5とか勉強会 JavaScript MVCフレームワーク まとめ


第31回 HTML5とか勉強会に参加してきました。
そこでメモっておいたことをまとめたので紹介します。

テーマ:JavaScript MVCフレームワーク

アジェンダ

  • MVCフレームワークの紹介
  • 全員参加の座談会:40分 モデレータ:新野淳一さん(publickey)
  • 質疑応答など10分

togetterのまとめ: http://togetter.com/li/336924

MVCフレームワークの紹介

Backbone.js

実績:LinkedIn、FourSquare
なんでもかんでもMVCが必要というわけではない(?)
underscore.jsに依存 → ユーティリティライブラリ、単体でも便利
Backbon.View, Model, CollectionはすべてBackbone.Eventsを継承
データの同期にRESTfulAlAPI
backbone.io:node.jsとsocket.ioでサーバサイドと連携。リアクティブプログラミング可能
Simpleなのでソースコードを読めばいろいろ解決することが多い

Spine.js

backbone.jsにインスパイアされて作成された。
「ステートフルJavaScript」の著者が作成。
現在のSpine.jsはCoffeeScriptでの開発が前提
CSSはStylus
■特徴
  • Simple: シンプルなMVCアーキテクチャ
  • Lightweight: 依存ライブラリなし。ただしjQueryかZepto.jsの使用を推奨
  • CoffeeScript: CoffeeScriptは簡潔でわかりやすい。(class型のオブジェクト指向なのでJava系の言語使ったことある人には○)
spine.appでプロジェクトのひな形を作成可能
hemでローカルサーバ構築、ホットデプロイで簡単にデバッグ可能
CoffeeScriptとStylusをビルドして、application.jsとapplication.cssを作成 → jsとcssが各一つなのでhtmlヘッダがすっきり
ModelはStorage機能を併せ持つ。簡単にlocalStorageとServerの切り替えが可能
Railsと相性がいい

Ember.js

SproutCoreがベース(AppleがMobileMeとかで利用してた)
Models: アプリケーションのデータ構造を表現。Model動詞の関連を含む
Views: handlebar.jsというテンプレートエンジンを利用。高速。イベントハンドリングとDOMのアップデート
Controllers: オブジェクトリスト(Modelオブジェクト)の管理
StateManager: 各MVCパーツのミディエータ。アプリの地図。ナビゲータ
■特徴
  • 同じようなコードを書かない
  • 標準的なアプリ構造を提供
    • 双方向のバインディング (Model <-> View)
    • 関数をプロパティのように扱う
    • 自動で更新するテンプレート

Angular.js

実績: doubleclickのどこかに使われている
初めての人にも手が出しやすい
双方向データバインディング(MDV: Model Driven View)
DOM構造と直感的に結びつくControllerの継承関係
JasmineやJsTestDriverでテストしやすい作り
i18nに対応
HTMLセントリックの開発(ライブコーディングしてたけど、JSはほとんど書かなかった)
Controllerの中に定義するプロパティはすべてModel
ng-hogeの「ng」はA"ng"ular

座談会

フレームワークを利用して、アプリケーションは遅くならないか?

backbone,js: 軽量なので感じない。EventをDelegateしないでDOMに直接アタッチしたりすると重くなるかも
Spine.js: 軽量なので感じない。通信は非同期で行われるので、ストレスはない
Ember.js: ライブラリ自体が重いので、ロードに時間がかかる。
               小さなサービスでは使わないほうがいいかも。ただ、生のJavaScriptよりは遥かに整理して書けるため、大規模アプリでは利点の方が大きい。
Angular.js: 自分のアプリをAngular.jsで書きなおしてみたが、ちょっと遅くなった気が…
Ember.jsやAngular.jsは生産性重視

UIバインディングの利点?

UIバインディング有(Angular.js / Ember.js): 理解しやすい。コード量が減る
UIバインディング無(Backbone.js / Spine.js): 自分でテンプレートエンジンを選んだり書いたりできるので、バインディングの仕組みが理解しやすいし、カスタマイズしやすい。

他のライブラリとの組み合わせは?

backbone.js: jQuery (あるいはZepto.js等jQuery互換のもの)推奨。テンプレートエンジンはなんでも
Spine.js: jQuery (あるいはZepto.js等jQuery互換のもの)推奨。テンプレートエンジンはなんでも
Ember.js: jQueryが利用されることが多い。しかしZepto.js等jQuery互換のものも使える。なくても大丈夫。
               jQueryは主にフレームワークに含まれていないAjax機能を利用。
Angualr.js: フレームワーク自体にjQ-liteというjQuery互換のライブラリが含まれている。
                ただ、外部からjQueryを読み込むとそちらでオーバーライドされる。

Modelとサーバの連携は?

Angular.js: サーバとは疎結合。httpサービス使って自分で
Ember.js: railsと相性いい
Spine.js: RESTfull
Backbone.js: backbone.syncを使ってRESTfullに。
railsと相性いい。backbone.ioを使ってnode.jsともいける。

質疑応答

チームで使う時の生産性とか学習曲線とか

backbone.js: MVCがわかってればおk
Spine.js: CoffeeScriptの知識が必要。シンプルなので、フレームワーク自体はわかりやすい
Ember.js: ルールがわかるまでちょっと時間かかるが、わかれば複数人でも生産性上がる
Angular.js: いろいろ簡単にできるが、複雑なことをやろうとすると難しいかも。
                分業はしやすいのではないか。(html作る人、コントローラ作る人、みないな?)

ドキュメントやコミュニティは?

Angular.js: ドキュメントは充実しているが、英語。
                サンプルはかなり充実している。
                 英語のコミュニティは活発。日本語のコミュニティも作られる、かも…?
Ember.js: コミュニティはGithubで活発。
               ドキュメントは少ない。ソースコード読め。
Spine.js: ドキュメントは充実しているが、英語。
Backbone.js: ドキュメントは英語。
                    コミュニティは英語のみだが活発。
                    小さいフレームワークだからソースコード読むのが一番

スマートフォン向けに使えるか?

Angular.js / Ember.js: ライブラリ自体が大きいため、読み込みに時間がかかってしまう。
                                スマートフォンはあまり考慮されていない
Spine.js: Spine mobileというスマートフォン向けライブラリがある
Backbone.js: 特に何も用意されていない。しかし、jQueryではなくZepto.jsを使えば、backbone.js自体も軽量なので良い感じなのでは?
------------------------------------------------------------------------
以上です。
最近はスマートフォン向けのサービスを作っているので、Backbone.jsやSpine.jsはいじってみようかなあ、などと思いつつ。

2012年6月27日水曜日

[enchant-js] iPhoneで画面幅からページがはみ出てしまう場合の対処方法

Android/iPhone向けのゲームをenchant.jsで作っていた時にハマったのでメモ。

enchant.jsは自前でviewportを設定し、iPhone4のRetina対応をしようとします。

しかし、ページのサイズを固定したかったり、ページの一部だけをenchant.jsで作りたい時はこの設定が邪魔になります。

私の場合は
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0" />
とhtmlで指定していたにもかかわらず、iPhone上で画面サイズが大きくなりすぎてしまい一日くらい悩みました。
htmlの各要素のサイズは320pxからはみ出てないのに、なぜiPhoneだけ横スクロールしてしまうのか、と。

そこで、強引ではありますがenchant.jsのviewportを設定している部分をコメントアウトして対応しました。こんな感じです。
// enchant.js line 144
var RETINA_DISPLAY = (function() {
    if (navigator.userAgent.indexOf('iPhone') != -1 && window.devicePixelRatio == 2) {
        var viewport = document.querySelector('meta[name="viewport"]');
        if (viewport == null) {
            viewport = document.createElement('meta');
            document.head.appendChild(viewport);
        }
     //↓これをコメントアウト。 viewportはhtml上で設定しているため不要
        //viewport.setAttribute('content', 'width=640px');
        return true;
    } else {
        return false;
    }
})();
もっとスマートな方法はあるでしょうが、とりあえずこれでひとまず解決です。

2012年6月5日火曜日

enchant.jsで画面を自動拡大しない方法

enchant.jsでは画面サイズいっぱいにゲーム領域を表示するのが標準の動作です。
しかし、固定サイズでゲーム領域を表示し、残った部分に普通のHTML要素を表示したい、ということもあるかと思います。
そんな時はhtmlに下記のように記述すればOKです。
<body>
    <div id="enchant-stage">
    </div>

    <!-- ↓任意のhtml -->

</body>
通常はenchant.jsが自動で生成するidが「enchant-stage」のタグを先に書いておくと、コードで指定した通りの大きさでゲーム領域が表示されます。

2012年3月14日水曜日

[JavaScript] sleepを擬似的に実装

こんな感じ。
引数はミリ秒。
Dateオブジェクトを利用して、現在時刻がsleepを呼んだ時の日時+引数 を経過するまでループを回す。
ただし画面が固まるので利用する場面はほとんどないんじゃないかな。

function sleep(milliSeconds) {
    var startTime = new Date().getTime();
    while (new Date().getTime() < startTime + milliSeconds);
}

2012年2月11日土曜日

[JavaScript] 秒から時分秒を算出

秒で格納してある値を時分秒で表示したかった。
こんな感じ。

var formatTime = function(seconds) {
    var result = "";
    //時間計算
    var hour = Math.floor((seconds / 60) / 60);
    var min = Math.floor((seconds / 60) % 60);
    var sec = Math.floor(seconds % 60);
    //フォーマット

    if (hour > 0) {
        result += hour + "時";

    }
    if (min > 0) {
        result += min + "分";
    }
    if (sec > 0) {
        result += sec + "秒";

    }
    return result;
}