LoginSignup
24
23

More than 5 years have passed since last update.

jsファイルの依存性管理について

Last updated at Posted at 2014-09-18

requireJS

js ファイルの管理は requireJS が有名だ。
requireJS というのはかいつまんで言うと、

index.html
<html>
  <head></head>
  <body>
    <script src="dondon.js"></script>
    <script src="dondoko.js"></script>
    <script src="dondokodon.js"></script>
    <script src="dokodondon.js"></script>
    <script src="dontakata.js"></script>
    <script src="dokatantan.js"></script>
    <script src="dokadoka.js"></script>
    <script src="dokashaba.js"></script>
    <script src="shabadosu.js"></script>
    <script src="shabadobi.js"></script>
    <script src="shabadobia.js"></script>
    <div>some contents</div>
  </body>
</html>

こいつを

index.html
<html>
  <head></head>
  <body>
    <script src="scripts/require.js" data-main="scripts/main.js"></script>
    <div>some contents</div>
  </body>
</html>
scripts/main.js
require.config({
  shim: {
    dondokodon: ["dondon", "dondoko"],
    dondokodon: ["dondoko"],
    dokatantan: ["dondokodon", "dokodondon", "dontakata"],
    dokadoka: ["dokatantan"],
    shabadobi: ["dondon"],
    shabadobia: ["dokatantan", "dokashaba", "shabadosu", "shabadobi"]
  },
  deps: ["shabadobia"]
});

こうしてくれる便利なモジュールだ。

両者の違いは、

  • 前者:
    • ファイルリスト(= 「上にあるほど優先度高いよ」)によって管理している
  • 後者:
    • ファイル(= 「このファイルを読み込む前に」)と、
    • ファイルリスト(= 「このファイル群が読み込まれている必要がありますよ」)の組によって管理している

「依存性」というのはまさにこの「ファイルとファイルリストの組」のことなので、後者のほうがより本質的な表現と言える。
(余談だがファイルに限らず、依存関係とは $X \times [X]$ の部分集合のことである、と定義できる)

requireJS には ファイル管理と同時にモジュールを管理する方法も提供されているが、angularJS の Dependency Injection の精神とバッティングしているので、angularJS と requireJS を併用する際には使用しない(その辺の考察はこちらを参考にした: https://github.com/CaryLandholt/AngularFun#commentary

商用 WEB アプリケーション開発の際の問題点

さて、開発の際には requireJS はとても便利なソリューションだが、実際に商用の WEB アプリケーションを作る上では通信コストを考慮しなければならない。モジュールごとにファイルを細かく分割しておきたいが、公開の際には、

  • ファイル結合
  • minify
  • gzip 圧縮

して提供したい。

ファイル結合さえ出来てしまえば minify, gzip 圧縮は grunt のタスクとしてそういうのがあるので、あんまし考えることは無い。
問題は、

  • 開発ビルド時は「ファイルとファイルリストの組」をもとに main.js を生成
  • 商用ビルド時は「ファイルとファイルリストの組」をもとに「結合する順序」を決定

これを、grunt で勝手にやってくれないものだろうか?

grunt-file-dependency

ざっと探した(あんまり探してない)感じだとよさげなのがなかったので作った↓

こいつを Gruntfile.js に取り込むと、次の2つのタスクが使えるようになる。

  • makeMainJs
    • 「ファイルとファイルリストの組」から、main.js を生成する
    • 実際には Gruntfile.js で指定した オブジェクト を、JSON.stringify して require.config にぶち込んだものを出力しているので、shim プロパティに「ファイルとファイルリストの組」を渡した上で、必要に応じて paths, deps などもGruntfile.js から渡す。
  • makePriority
    • 「ファイルとファイルリストの組」から、整列されたファイルリスト(配列)を出力する
    • こいつを、concat タスクの src として渡し、同タスクを実行すれば、concat されて出力される。

Gruntfile.js の例

(function() {
var reqConfig = {
  baseUrl: "scripts/",
  paths: {
    a: "hoge/fuga"
    b: "fizz/buzz"
    // ...
  },
  shim: {
    a: ["b", "c", "d"]
    b: ["c", "e", "f"]
    e: ["g", "h"]
    i: ["a", "b", "e", "j"]
  },
  deps: ["i"]
};

module.exports = function(grunt) {
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-file-dependnecy');
  grunt.initConfig({
    copy: {
      dev: {
        expand: true,
        cwd: "src/scripts/",
        src: "**/*.js",
        dest: "build/dev/scripts/"
      }
    },
    makeMainJs: {
      dev: {
        dest: "build/dev/scripts/main.js",
        main: reqConfig
      }
    },
    makePriority: {
      dist: {
        options: {
          baseUrl: "src/scripts",
          alias: reqConfig.paths
        },
        deps: reqConfig.shim,
        done: function(priority) {
          grunt.config.set("concat.dist.src", priority);
        }
      }
    },
    concat: {
      dist: {
        dest: "vendor/dist/scripts/scripts.js"
      }
    }
  });

  grunt.registerTask("build:dev", ["copy:dev", "makeMainJs:dev"]);
  grunt.registerTask("build:dist", ["makePriority:dist", "concat:dist"]);
};
})();

なお readme.md はそのうち書きます。

24
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
23