GruntでJavascriptを圧縮できる、grunt-contrib-uglifyの使い方まとめ

こんにちは、つみきのフロントエンドエンジニアの佐藤です。

今回は grunt-contrib-uglify について、導入方法から使い方まで解説してみます。

見出し

動作環境

  • OSX 10.9.3
  • node v0.10.26
  • npm 1.4.3
  • grunt-cli 0.1.13
  • grunt v0.4.5
  • grunt-contrib-uglify v0.5.0

参考

grunt-contrib-uglify

grunt-contrib-uglify は UglifyJS という JavaScript のパースや圧縮を行うツールを Grunt 用にモジュール化したものです。

jQuery もバージョン 1.5 から利用しているようです。

導入方法

Grunt の利用環境が整っていない場合はまず先にやります。

Node.js と npm (node package manager) 、 Grunt のコマンドラインインターフェイスをインストールをします。

Node.js, npm

それぞれの環境に適した方法でインストールしてください。公式サイト からインストーラーをダウンロードする方法が簡単です。

また、バージョン管理も行える nodebrewnvm などのバージョンマネージャーも便利かと思います。 筆者は nodebrew を利用しています。

Grunt CLI

ターミナルやコマンドプロントから以下のコマンドを入力します。

npm install -g grunt-cli

管理者権限がない場合は sudo コマンドで実行します。(Windows の場合は「管理者権限で実行」をしてください)

sudo npm install -g grunt-cli

これで grunt コマンドを利用できるようになります。

grunt-contrib-uglify をインストール

作業ディレクトリに移動し、以下のコマンドで grunt-contrib-uglify をインストールします。

npm install grunt-contrib-uglify --save-dev

Grunt のコアモジュールがインストールされていない場合は一緒にインストールしてくれます。

引数に --save-dev--save オプションを指定すれば、 package.json にプロジェクトの依存モジュールとして追記してくれます。

別プロジェクト等で再利用する際に、 npm install コマンドで記載モジュールをインストールしてくれるので便利です。

タスクの設定

Gruntfile.jsを用意

次に、タスクの定義や細かい設定を記載する Gruntfile.js を用意します。

この JavaScript ファイルを Grunt が読み込んで実際にタスクを実行できるようになります。

// Gruntfile.js
module.exports = function(grunt) {

  // タスクの設定
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
      },
      build: {
        src: 'src/<%= pkg.name %>.js',
        dest: 'build/<%= pkg.name %>.min.js'
      }
    }
  });

  // モジュールの読み込み
  grunt.loadNpmTasks('grunt-contrib-uglify');

  // タスクの登録
  grunt.registerTask('default', ['uglify']);

};

これで grunt コマンドで、

  • src/ ディレクトリ内にある、
  • package.json ファイルに記載された name プロパティの値の名前の JavaScript ファイルが、
  • 冒頭に、 package.jsonname と、タスクを実行した日付をコメントで付け加えられ、
  • build/ という新しいディレクトリが作成され、
  • そこに圧縮された状態で生成される

という流れでタスクが実行されます。

オプション

いくつか指定できるオプションについて抜粋し簡単に解説します。

mangle

ローカル変数名を短い名称に変更し難読化します。

compress

冗長なコードを短くまとめてくれます。

beautify

改行、インデントは取り除かず、読みやすいように整形します。

report

'gzip'を指定すると gzip で圧縮されます。デフォルトは 'min' です。

sourceMap

ソースマップも合わせて出力します。ファイルの展開先と同じディレクトリに生成されます。

ソースマップは、ファイルのマッピングを解決してくれるので圧縮されたファイルでデバッグする際に便利です。

sourceMapName

ソースマップの生成先とファイル名を指定できます。 文字列をそのまま渡すか、関数でも渡せます。

sourceMapIn

CoffeeScript 等メタ言語を使用している際などに依存関係のある場合のマッピングに。

preserveComments

コメントの残し方を指定します。

  • 'all' ですべてのコメントを残します
  • 'some'/*! から始まるコメントだけ残ります。バナーやライセンス表記の際に便利です

banner, footer

指定文字列をファイル冒頭、もしくは下部に追加した状態で生成されます

使用例

実際の挙動です。

サンプル JavaScript のコードは以下です。

/*!
 * stabu.js
 * License MIT
 */
(function(exports) {
  var order, htmlResult;

  var Order = function(name, order) {
    this.name = name;
    this.order = order;
  };

  Order.prototype = (function() {

    var MENU = {
      coffee: 200,
      water: 300,
      tea: 400,
      bread: 600,
      juice: 500
    };

    return {
      getAmount: function() {
        var orders = this.order,
            result = [],
            key, i, l;

        // @todo
        for (i = 0, l = orders.length; i < l; i++) {
          for (key in MENU) {
            if (MENU.hasOwnProperty(key) &&
                orders[i] === key) {
              result[i] = MENU[key];
              break;
            }
          }
        }

        return result;
      },

      totalAmount: (function() {
        var counter = 0;

        return function() {
          var amounts = this.getAmount(),
              i, l, total = 0, result, num;

          for (i = 0, l = amounts.length; i < l; i++) {
            total += amounts[i];
          }

          if (!this.number) {
            this.number = ++counter;
            num = this.number;
          } else {
            num = this.number;
          }

          result = num + ' 人目の客 ' +
                   this.name + ' さんの合計金額 ' +
                   total + ' 円' + '<br>';
          return result;
        };
      })()
    };
  })();



  order = new Order('sato', ['coffee', 'water', 'tea']);

  htmlResult = document.getElementById('result');

  htmlResult.innerHTML += order.totalAmount();

  exports.Order = exports.Order || Order;

})(this);

まずはローカル変数短縮とコードをコンパクト化してみます。

module.exports = function(grunt) {

  grunt.initConfig({
    uglify: {
      options: {
        mangle: true,
        compress: true
      },
      build:  {
        src: 'stabu.js',
        dest: 'build/stabu.min.js'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');

  grunt.registerTask('default', ['uglify']);

};

実行します。

grunt uglify

Running "uglify:min" (uglify) task
File build/stabu.min.js created: 1.57 kB → 667 B

以下のようにミニファイされます。

!function(a){var b,c,d=function(a,b){this.name=a,this.order=b};d.prototype=function(){var a={coffee:200,water:300,tea:400,bread:600,juice:500};return{getAmount:function(){var b,c,d,e=this.order,f=[];for(c=0,d=e.length;d>c;c++)for(b in a)if(a.hasOwnProperty(b)&&e[c]===b){f[c]=a[b];break}return f},totalAmount:function(){var a=0;return function(){var b,c,d,e,f=this.getAmount(),g=0;for(b=0,c=f.length;c>b;b++)g+=f[b];return this.number?e=this.number:(this.number=++a,e=this.number),d=e+" 人目の客 "+this.name+" さんの合計金額 "+g+" 円<br>"}}()}}(),b=new d("sato",["coffee","water","tea"]),c=document.getElementById("result"),c.innerHTML+=b.totalAmount(),a.Order=a.Order||d}(this);

ファイルサイズが半分以下になりました。

preserveComments オプションを使い、 冒頭のコメントのみ残してみます。

module.exports = function(grunt) {

  grunt.initConfig({
    uglify: {
      options: {
        mangle: true,
        compress: true,
        preserveComments: 'some'
      },
      build:  {
        src: 'stabu.js',
        dest: 'build/stabu.min.js'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');

  grunt.registerTask('default', ['uglify']);

};

冒頭の /*! のコメントのみ残ります。

/*!
 * stabu.js
 * License MIT
 */
!function(a){var b,c,d=function(a,b){this.name=a,this.order=b};d.prototype=function(){var a={coffee:200,water:300,tea:400,bread:600,juice:500};return{getAmount:function(){var b,c,d,e=this.order,f=[];for(c=0,d=e.length;d>c;c++)for(b in a)if(a.hasOwnProperty(b)&&e[c]===b){f[c]=a[b];break}return f},totalAmount:function(){var a=0;return function(){var b,c,d,e,f=this.getAmount(),g=0;for(b=0,c=f.length;c>b;b++)g+=f[b];return this.number?e=this.number:(this.number=++a,e=this.number),d=e+" 人目の客 "+this.name+" さんの合計金額 "+g+" 円<br>"}}()}}(),b=new d("sato",["coffee","water","tea"]),c=document.getElementById("result"),c.innerHTML+=b.totalAmount(),a.Order=a.Order||d}(this);

また、圧縮はせずインデントを揃えて見やすくしたい場合にも使えます。

module.exports = function(grunt) {

  grunt.initConfig({
    uglify: {
      options: {
        mangle: false,
        compress: false,
        beautify: true
      },
      build:  {
        src: 'stabu.js',
        dest: 'dest/stabu.js'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');

  grunt.registerTask('default', ['uglify']);

};

インデントはスペース 4 つで綺麗にインデントされます。

まとめ

リソースのファイルサイズはサーバからの転送速度、 コードのパース速度はユーザの体感速度にそのまま影響します。

JavaScript の最適化は Web のパフォーマンス最適化の一つの手段として活用できるかと思います。

スマホアプリ制作、Web制作、UIコンサルなどのご依頼はこちら

お問い合わせ