やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

require.jsを使ってモジュール化する方法

記法がたくさんあって難しいので調べた。

モジュール化とは

 モジュール化とは、別ファイルに分割することである。

require.jsとは

require.jsとは、モジュール化するためのライブラリである。

requireとは

requireとは、「必要とする」という意味の動詞。

やってみた

以前やってみたが、じつは他にも記法がたくさんある。

require.jsの使い方

 公式サイトのAPIリファレンスを参照した。これを読めばマスターできるが、多すぎるので以下に抜粋する。

これさえあればいい

こんなときどう書く?

プラグイン

コード

構成

  • index.html
  • js/
    • app.js
    • lib/
      • require/
        • require.js
    • app/
      • main.js
      • sub.js

 main.jsからsub.jsを呼び出すのが狙い。

ファイル内容

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <script data-main="app.js" src="js/lib/require/require.js"></script>
</head>
<body></body>
</html>

app.js

require(['js/app/main.js']);

main.js

define(function(require, exports, module) {
    var sub = require('js/app/sub');
    sub.print("MESSAGE!!");
});

sub.js

define(function() {
    return {
        print: function(msg) {
            alert(msg);
            console.log(msg);
        }
    };
});

index.html

 data-main属性によりエントリポイントを指定している。

index.html

<script data-main="app.js" src="js/lib/require/require.js"></script>

 特殊な記法だが、ほかにもいくつかパターンがある。この方法が最もスマート。

app.js

 requireの設定をするときにmain.jsと分離しておきたいから用意した。たとえば以下のように分離する。

app.js

require.config({
    paths: {
        jquery: "js/lib/jquery/jquery-3.3.1.min.js"
    }
});
require(['js/app/main.js']);

main.js

define(function(require, exports, module) {
    var $ = require('jquery');
    $("body").html("Hello jQuery !!");
});

main.js

 CommonJS方式。

define(function(require, exports, module) {

 引数exports, moduleは使っていないので省略できる。

define(function(require) {

sub.js

define(function() {

 もし他のコードをインポートしたくなったらrequire引数を追加する。

define(function(require) {
    var $ = require('jquery');

 exports, moduleを追加してもいい。

define(function(require, exports, module) {

ES6 class

 ES6のclassが実装されてるなら使うといい。

main.js

define(function(require, exports, module) {
    const Human = require('js/app/Human');
    const h = new Human("Yamada");
    h.Speak();
});

Human.js

define(function() {
    return class Human {
        constructor(name=null){ this._name = name; }
        get Name() { return this._name; }
        set Name(v) { if(v){ this._name = v; } }
        Speak() {
            alert(this._name);
            console.log(this._name);
        }
    };
});

sub.jsをHuman.jsに置き換えた。classreturnしている。

モジュール化

 require.jsでモジュール化するコードの記法は場合によってたくさんある。以下のうち1.3.5 Define a Module with Simplified CommonJS Wrapperの方法が最も汎用的だと思う。

コード概要 説明URL
require(){...} 1.2 data-main Entry Point
define({...}) 1.3.1 Simple Name/Value Pairs
define(function () {... return {...};}) 1.3.2 Definition Functions
define(["./foo", "./bar"], function(foo, bar) {... return {...};}) 1.3.3 Definition Functions with Dependencies
define(["./foo", "./bar"], function(foo, bar) {... return function(){...};}) 1.3.4 Define a Module as a Function
define(function(require, exports, module) {... return {...};}) 1.3.5 Define a Module with Simplified CommonJS Wrapper
define("foo/title", ["a/A", "a/B"], function(A, B) {...}); 1.3.6 Define a Module with a Name
define(["require", "./relative/name"], function(require) { var mod = require("./relative/name"); }); 1.3.7 Other Module Notes
define(function(require) { var mod = require("./relative/name"); }); 1.3.7 Other Module Notes Better
define(["require", "a"], function(require, a) { return function(title) { return require("a").doSomething(); }});
or
define(function(require, exports, module) { var a = require("a"); exports.foo = function () { return a.bar(); }; });
1.3.8 Circular Dependencies 循環参照
require(["http://example.com/api/data.json?callback=define"], function (data) { console.log(data); }); 1.3.9 Specify a JSONP Service Dependency

比較

 以下のうちAのほうが好ましい。特にインポートするファイル数が多くなると顕著。

インポート数1

A
define(function (require) {
    var sub = require('js/app/sub');
    return function () {
        sub.print();
    };
});
B
define(['js/app/sub'] , function (sub) {
    return function () { sub.print(); };
});

インポート数3

A
define(function (require) {
    var sub1 = require('js/app/sub1');
    var sub2 = require('js/app/sub2');
    var sub3 = require('js/app/sub3');
    return function () {
        sub1.print();
    };
});

 インポートの追加や削除をするとき、行の編集だけで済む。

B
define(['js/app/sub1','js/app/sub2','js/app/sub3'] , function (sub1,sub2,sub3) {
    return function () { sub1.print(); };
});

 インポートの追加と削除をするとき、配列と引数をそれぞれ編集せねばならない。カンマの編集や、配列と引数の位置を合わせねばならない。ミスしそう。可読性が低い。

循環参照

 A, Bともに少し特殊な記述になる。

A

js/app/sub2.js

define(function(require, exports, module) {
    var sub1 = require("js/app/sub1");
    exports.foo = function () {
        return sub1.print(); // sub1.jsで本ファイルsub2.jsを参照する
    };
});

 sub2.jsでエクスポートしたい場合、exportsオブジェクトに生成する必要がある。(非循環参照の場合:エクスポートするオブジェクトはreturnで返していた)

B

js/app/sub2.js

define(['js/lib/require/require', 'js/app/sub1'] , function (require, sub1) {
    return function() {
        return require("js/app/sub1").print(); // sub1.jsで本ファイルsub2.jsを参照する
    }
});

 require.jsをインポートする必要がある。(非循環参照の場合:require.jsのインポートは不要だった)