Monthly Archives: June 2013

Require.jsを試してみた

Require.jsはActionScriptのimportのようにjsファイル同士の依存関係を崩さないよう非同期で読み込みつつ、多重読み込みなども防止してくれるライブラリ。
Require.jsを使えば細かくjsファイルを分割してもBackboneやjQuery等のライブラリを依存関係を保ちつつ読み込んでみる。

最小限のコードで挙動を確認してみる。(以降すべてCoffeeScript
これを実行するとMyModelのmessageに定義された「Hello, World!」が出力される。
Backboneに必要なUnderscoreとjQueryはBackboneより先に読み込まれている。

require([
	"libs/jquery-1.10.1.min"
	"libs/underscore.min"
	"libs/backbone.min"
],
($, _, Backbone) ->
	MyModel = Backbone.Model.extend({
		message: "Hello, World!"
	})

	MyView = Backbone.View.extend({
		initialize: (options) ->
			console.log @model.message
	})

	view = new MyView({
		model: new MyModel()
	})
	@
)

今度はRequire.jsの機能のdefineを使ってモジュールを作ってみる。
こちらも同じく「Hello, World!」が出力される。

define("myModule",
[
	"libs/jquery-1.10.1.min"
	"libs/underscore.min"
	"libs/backbone.min"
],
($, _, Backbone) ->
	MyModel = Backbone.Model.extend({
		message: "Hello, World!"
	})

	MyView = Backbone.View.extend({
		initialize: (options) ->
			console.log @model.message
	})

	return {
		modelClass: MyModel
		viewClass: MyView
	}
)

require([
	"myModule"
],
(module) ->
	model = new module.modelClass()
	view = new module.viewClass({
		"model": model
	})
	@
)

require.configのshimでライブラリの依存関係をあらかじめ設定してみる。
実行結果は同様。

require.config(
	shim:
		"libs/backbone.min":
			deps: [
				"libs/jquery-1.10.1.min"
				"libs/underscore.min"
			]
			exports:
				"Backbone"
)

define("myModule",
[
	"libs/backbone.min"
],
(Backbone) ->
	MyModel = Backbone.Model.extend({
		message: "Hello, World!"
	})

	MyView = Backbone.View.extend({
		initialize: (options) ->
			console.log @model.message
	})

	return {
		modelClass: MyModel
		viewClass: MyView
	}
)

require([
	"myModule"
],
(module) ->
	model = new module.modelClass()
	view = new module.viewClass({
		"model": model
	})
	@
)

今度はrequire.configのpathsを使ってライブラリのパスをあらかじめ定義しておく。
こちらも実行結果は同様。

require.config(
	paths:
		"jquery": "libs/jquery-1.10.1.min"
		"underscore": "libs/underscore.min"
		"backbone": "libs/backbone.min"
)

define("myModule",
[
	"jquery"
	"underscore"
	"backbone"
],
() ->
	MyModel = Backbone.Model.extend({
		message: "Hello, World!"
	})

	MyView = Backbone.View.extend({
		initialize: (options) ->
			console.log @model.message
	})

	return {
		modelClass: MyModel
		viewClass: MyView
	}
)

require([
	"myModule"
],
(module) ->
	model = new module.modelClass()
	view = new module.viewClass({
		"model": model
	})
	@
)

今度はshimにもpathsで定義したパスを使ってみる。

require.config(
	paths:
		"jquery": "libs/jquery-1.10.1.min"
		"underscore": "libs/underscore.min"
		"backbone": "libs/backbone.min"

	shim:
		"backbone":
			deps: [
				"jquery"
				"underscore"
			]
			exports:
				"Backbone"
)

define("myModule",
[
	"backbone"
],
() ->
	MyModel = Backbone.Model.extend({
		message: "Hello, World!"
	})

	MyView = Backbone.View.extend({
		initialize: (options) ->
			console.log @model.message
	})

	return {
		modelClass: MyModel
		viewClass: MyView
	}
)

require([
	"myModule"
],
(module) ->
	model = new module.modelClass()
	view = new module.viewClass({
		"model": model
	})
	@
)

require.configでの設定は一度きりなのでこれを無視すると、メインのコードはだいぶスッキリした。

参考:
RequireJS moduleについて – 文殊堂
java-ja.js #2 RequireJS実践編 – 文殊堂
[JS] JavaScriptをモジュール分割して開発できるRequireJSに入門 – YoheiM .NET

Grunt導入してみた

MacにはCodeKitっていうCoffeeScriptとかSassとかLessとかをコンパイルしてくれる便利なツールがあって最近までお世話になってた。でも自分はWindowsユーザーで、これだけのためにMacを起動するのも面倒くさいので、CodeKitでやってたことをWindowsでもできるようにとGruntをはじめてみた。

Gruntそのものはコンパイラではなく、いろんなタスクを実行してくれるだけのツールなんだけど、プラグインを追加することでCoffeeScriptのコンパイルや、Sass/Lessのコンパイル、Uglifyを使ってjsのminifyなどができるようになる。

これを使ってCodeKitでやってたことを再現してみた。

1. インストール

まずGruntを実行するのに必要なnode.jsをインストール。同時にnpm(node.js用のパッケージ管理ツール)もインストールされる。

環境変数PATHに「C:\Users\{ユーザー名}\AppData\Roaming\npm;C:\Program Files\nodejs\」を追加しておく。

gruntをインストール

npm install -g grunt-cli

※「npm install -g grunt」ではない

環境変数PATHに「grunt」を追加しておく。

参考:
Windows 環境変数 Path の設定方法 – コンピュータ/ソフトウェア関連Tips
はじめに | Grunt 日本語リファレンス | js STUDIO

2. プロジェクトの準備

プロジェクトのルートフォルダにGruntfile.js(もしくはGruntfile.coffee)と、package.jsonというファイルを作る。
Gruntfile.js(Gruntfile.coffee)はGruntに実行させるタスクを定義しておくファイル。package.jsonはプロジェクトに必要なGruntのプラグインのリスト。

3. package.json

package.jsonはこのように記述して、devDependenciesに必要なプラグイン(パッケージ)の名前とバージョンを羅列しておく。

{
  "name": "{プロジェクト名}",
  "version": "{パッケージのバージョン}",
  "author": "{作者}",
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-livereload": ">=0.0.0",
    "grunt-contrib-watch": ">=0.0.0",
    "grunt-contrib-coffee": ">=0.0.0",
    "grunt-contrib-uglify": ">=0.0.0",
    "grunt-contrib-jshint": ">=0.0.0",
    "grunt-contrib-jst": ">=0.0.0",
    "grunt-contrib-sass": ">=0.0.0",
    "grunt-contrib-compass": ">=0.0.0",
    "grunt-contrib-cssmin": ">=0.0.0"
  }
}

package.jsonを用意した状態で「npm install」をコマンドプロンプト上で実行すると、devDependenciesに書かれたプラグインを順番にプロジェクトフォルダ直下のnode_modulesフォルダにダウンロードしてきてくれる。

npm install

と書かれた.batファイルを作っておくと良い。

参考:
npm-json(1) — Specifics of npm’s package.json handling
npm と package.json でパッケージ管理 | FIRN.JP
npmとpackage.json使い方 – Kokudoriing

4. Gruntfile.js

下記のようなラッパー関数を用意し、その中にタスクを書いていく。Gruntfile.jsの内容はGruntを通してnode.js上で実行されるので、js(もしくはCoffee Script)で記述していく。

module.exports = function(grunt)
{
  //ここにGruntに実行させたいタスクを書いていく
};

coffeeファイルが入ったフォルダを監視してcoffeeファイルが更新されたらCoffeeScriptをコンパイルして、uglifyでminifyするGruntfile.coffee。

module.exports = (grunt) ->

    # load tasks
    grunt.loadNpmTasks "grunt-contrib-coffee"
    grunt.loadNpmTasks "grunt-contrib-uglify"
    grunt.loadNpmTasks "grunt-contrib-watch"

    grunt.initConfig(

        coffee:
            files:
                expand: true
                cwd: "assets/coffee/"
                src: ["**/**/*.coffee"]
                dest: "assets/js/"
                ext: ".js"

        uglify:
            files:
                expand: true
                cwd: "assets/js/"
                src: ["*.js"]
                dest: "assets/js/"
                ext: ".min.js"
            options: ""

        watch:
            coffee:
                files: ["assets/coffee/**/*.coffee"]
                tasks: [
                    "coffee"
                    "uglify"
                ]
	)

    grunt.registerTask "default", ["watch"]

sassファイルが入ったフォルダを監視してsassファイルが更新されたらSassをコンパイルして、sprite画像も生成する。

module.exports = (grunt) ->

    # load tasks
    grunt.loadNpmTasks "grunt-contrib-compass"
    grunt.loadNpmTasks "grunt-contrib-cssmin"
    grunt.loadNpmTasks "grunt-contrib-watch"

    grunt.initConfig(

        compass:
            dist:
                options:
                    environment: "development"
                    outputStyle: "compressed"
                    debugInfo: true
                    relativeAssets: true
                    basePath: "assets"
                    sassDir: "sass"
                    cssDir: "css"
                    generatedImagesDir: "images"

        cssmin:
            files:
                expand: true
                cwd: "assets/css"
                src: ["*.css", "!*.min.css"]
                dest: "assets/css/"
                ext: ".min.css"

        watch:
            sass:
                files: ["assets/sass/**/*.sass"]
                tasks: [
                    "compass"
                    "cssmin"
                ]
	)

    grunt.registerTask "default", ["watch"]

これらをまとめつつ、JavaScriptテンプレートのコンパイル、htmlの更新監視にコンパイル後にLiveReloadを使ってブラウザをリロードするタスクを加えるとこうなる。

module.exports = (grunt) ->

    # load tasks
    grunt.loadNpmTasks "grunt-contrib-compass"
    grunt.loadNpmTasks "grunt-contrib-cssmin"
    grunt.loadNpmTasks "grunt-contrib-coffee"
    grunt.loadNpmTasks "grunt-contrib-uglify"
    grunt.loadNpmTasks "grunt-contrib-jst"
    grunt.loadNpmTasks "grunt-contrib-watch"

    grunt.initConfig(
        compass:
            dist:
                options:
                    environment: "development"
                    outputStyle: "compressed"
                    debugInfo: true
                    relativeAssets: true
                    basePath: "assets"
                    sassDir: "sass"
                    cssDir: "css"
                    generatedImagesDir: "images"
        cssmin:
            files:
                expand: true
                cwd: "assets/css"
                src: ["*.css", "!*.min.css"]
                dest: "assets/css/"
                ext: ".min.css"
        coffee:
            files:
                expand: true
                cwd: "assets/coffee/"
                src: ["**/**/*.coffee"]
                dest: "assets/js/"
                ext: ".js"
            options:
                sourceMap: true
        jst:
            compile:
                options:
                    processName: (filename) ->
                        return filename.match(/assets\/jst\/(.*).js$/)[1]
                files:
                    "assets/js/templates.js": ["assets/jst/**/**/*.js"]
        uglify:
            files:
                expand: true
                cwd: "assets/js/"
                src: ["*.js"]
                dest: "assets/js/"
                ext: ".min.js"
            options: ""
        watch:
            html:
                files: ["**/*.html"]
                options:
                    livereload: true
            coffee:
                files: ["assets/coffee/**/*.coffee"]
                tasks: [
                    "start"
                    "coffee"
                    "uglify"
                ]
                options:
                    livereload: true
            jst:
                files: ["assets/jst/**/*.js"]
                tasks: [
                    "jst"
                    "uglify"
                ]
                options:
                    livereload: true
            sass:
                files: ["assets/sass/**/*.sass"]
                tasks: [
                    "start"
                    "compass"
                    "cssmin"
                ]
                options:
                    livereload: true
    )

    # register tasks
    grunt.registerTask "start", "start task", ->
        grunt.log.writeln "--------------------------------------------------------"
        grunt.log.writeln "    start task"
        grunt.log.writeln "--------------------------------------------------------"

    grunt.registerTask "default", ["watch"]

それぞれのタスクの設定はそれぞれのプラグインのページを参照のこと。

参考:
grunt-contrib-coffee
grunt-contrib-jst
grunt-contrib-uglify
grunt-contrib-compass
grunt-contrib-cssmin
grunt-contrib-watch

ExternalInterface.callが重い

Flashから毎フレームExternalInterface.callでJSの関数を呼び出したりしている時に、そのJSの関数内でなにかしらのObjectをreturnしてるとその処理に手間がかかるのか、Flashのフレームレートがすごい落ちる。

CoffeeScriptで関数の最後に@を入れてるとこの現象が起こりやすいので、ExternalInterface.callで直接呼び出される関数の最後はreturn falseして回避しておく。

さらにそのJSの関数内でDOMの操作みたいなちょっと重めの処理(width, height変えたり、left, top変えたり)をしてたりしても同様の現象が起こる。この場合はJSの関数内でやっている処理をsetTimeout(Function, 0)でくるんで非同期で処理してあげると回避できた。

なんだろこれ?ExternalInterface.callで呼び出した処理が終わるのをFlashが待っちゃうからかな?

追記1:
こういうことらしい

追記2:
CoffeeScriptで行末returnしないようにするには「return」だけ書いとけばいいっぽい。

近況0604

書き終わった瞬間の風景
前回から毎週日記書こうと思ったのにいきなり頓挫してしまった。今回は日常の隙をついて、会社帰りに歩きながらiPhoneで書いてみる。アプリで書いてるのでリンク貼るとかハイパーテキストっぽいことはめんどくさいので割愛。

先週末、先々週末は撮影のサポート。一日中撮影ポイントを探しつつ都内をウロウロ。座り仕事で体が錆びついてるせいか毎夜毎夜全身筋肉痛。

両親が結婚30周年で来京。宿泊先の帝国ホテルのスウィートがすごかった。

torne+nasneがいい感じ。torneは全ユーザーの視聴データや、録画予約のデータをもとに、各番組にミルネとトルネという独自のスコアをつけている。試しにこのスコアの高い番組をいろいろ録画してみた。このIT化を機にまたテレビを見る気が湧いてきた。

去年の年末ぐらいに、社長から引き継いだコードを読んでて、モーションプログラムのコツみたいなのに気づいた瞬間があった。それを意識しだしてからなんとなく自分がイメージしたようにオブジェクトが動いてくれるようになってきた。コツと言ってもただ単にモノを動かす時に、x、yじゃなくて距離と角度を操作するってだけの話なんだけど。

そんな感じ。