web-dev-qa-db-ja.com

Require.jsは私の頭を痛めています。スクリプト/モジュールをロードする方法に関するいくつかの基本的な質問

これが私のconfig.jsまたはmain.jsだとしましょう:

_require.config({
    // paths are analogous to old-school <script> tags, in order to reference js scripts
    paths: {
        jquery: "libs/jquery-1.7.2.min",
        underscore: "libs/underscore-min",
        backbone: "libs/backbone-min",
        jquerymobile: "libs/jquery.mobile-1.1.0.min",
        jquerymobilerouter: "libs/jquery.mobile.router.min"
    },
    // configure dependencies and export value aliases for old-school js scripts
    shim: {
        jquery: ["require"],
        underscore: {
            deps: ["jquery"],
            exports: "_"
        },
        backbone: {
            deps: ["underscore", "jquery"],
            exports: "Backbone"
        },
        jquerymobilerouter: ["jquery", "backbone", "underscore"],
        jquerymobile: ["jquery", "jquerymobilerouter", "backbone", "underscore"]
    }
});
require(["jquery", "backbone", "underscore", "app/app.min", "jquerymobilerouter", "jquerymobile"], function ($, Backbone, _, App) {
    console.log($);
    console.log(Backbone);
    console.log(_);
    $("body").fadeIn(function () {
        App.init();
    });
});
_
  1. 私が正しく理解している場合、paths configオプションを使用すると、HTML内の_<script>_タグを使用してスクリプトを参照できます。これが事実であると仮定しても、以下の実際のrequireステートメントで_$_を使用したjQueryのようなスクリプトや___を使用したアンダースコアを引き続きエイリアスする必要がありますか?標準の_<script>_タグでjQueryを参照する場合、_$_をスクリプト全体で自動的に使用できることを考えると、私がしなければならないのは奇妙に思われます。 pathsを使用しても同じではないですか?

  2. 私はshim設定オプションを初めて使用します。これは、廃止された_order!_プラグインを置き換えたことを理解しています。 exportsプロパティは実際には何をしますか?スクリプトのエイリアスを作成していないようです。たとえば、アンダースコアのexportsを_"whatever"_に設定してからconsole.log(whatever)にしようとすると、定義されません。それで、ポイントは何ですか?

  3. JQueryのようなスクリプトはどのように「グローバルに」適切に使用されますか?つまり、私のApp.jsモジュール内の_$_エイリアス、または「app」フォルダー内の他のモジュールを使用できる適切な方法は何ですか?個々のモジュールごとにjQueryを必要とし、毎回_$_をエイリアスする必要がありますか?または、私がここで行った方法は適切な方法ですか?

この特定のスクリプトに対する他の批判も大いに感謝します。私の意見では、Require.jsのドキュメントには多くのことが残されています。私が本当に知りたいことは、つやが出てきて頭を悩ませているようです。

45
J. Ky Marsh
  1. パスはrequire.jsに、その依存関係が必要なときにどこを探すべきかを伝えます。

    たとえば、私は次のように構成されています:

    _"paths": { 
        "jquery": "require_jquery"
    },
    "shim": {
        "jquery-cookie"  : ["jquery"],
        "bootstrap-tab"  : ["jquery"],
        "bootstrap-modal": ["jquery"],
        "bootstrap-alert": ["jquery"]
    },
    _

    つまり、モジュールで毎回

    _define( ['jquery']
    _

    requirejsは、jquery.jsをロードする代わりに、メインパスから_require_jquery_ファイルをロードします。あなたの場合、それはjQueryソースファイルをロードし、グローバルに利用可能になります。私は個人的にそのアプローチが好きではないので、require_jquery.jsファイルで次のようにしています。

    _define( ["jquery_1.7.2"], function() {
        // Raw jQuery does not return anything, so return it explicitly here.
        return jQuery.noConflict( true );
    } );
    _

    つまり、jQueryは私のモジュール内でのみ定義されます。 (これは、Wordpressプラグインを記述しているため、外部バージョンに手を加えることなく、自分のバージョンのjQueryを含めることができるためです)

  2. エクスポート(ドキュメントからの読み取りは、ロードが正しく行われたかどうかを検出できるように、使用しているモジュールの名前である必要があります。 ここ が説明されています。したがって、アンダースコアにエクスポートを設定する場合は、 ___である必要があります

  3. 説明したように、jQueryはグローバルである必要があります。単にインポートすると、ファイルが実行され、jQueryはグローバルになります。

編集-コメントに答えます。

  1. はい、つまり、jQueryの場合は$またはjQuery、バックボーンの場合は_をエクスポートする必要があります。私がドキュメントから得たものから、これは一部のEdgeケースでのみ必要であり、グローバルネームスペースでjQueryとして宣言されているライブラリには必要ありません。

    CDNからjQueryをロードすることからフォールバックする必要がある場合、requirejsはそれらを必要とすると思います。 requirejsは最初にCDNからjQueryをロードしようと試み、次に「エクスポートされた」変数が存在することをチェックすることによってそれが正しくロードされたことを確認するためのチェックを行い、それがローカルファイルシステムからロードしない場合は(それがもちろんフォールバックを設定していました)。これはrequirejsが404が戻ってくるのを見ることができないときに必要です。

  2. jQueryはグローバルに宣言されているため、グローバルに利用できます。 jQueryスクリプトを単にロードして実行すると、2つのグローバル、_$_とjQueryが作成されます(または、私が行ったようにしてそれを回避できます)。 define()関数内では、jQueryにエイリアスを設定して、好きなようにすることができます。

    _define( [ 'jquery' ], function( jq ) {
        // jq is jquery inside this function. if you declared it 
        // globally it will be also available as $ and jQuery
    } );
    _
22

exportsの混乱を解消するために、shimライブラリはプロパティをグローバルコンテキストにアタッチするか(windowまたはroot)、または既存のグローバルプロパティ(jQueryプラグインなど)を変更すると想定されています。 requireJSがシムされた依存関係をロードするコマンドを取得すると、shim構成のexports値に一致するプロパティのグローバルコンテキストを調べ、見つかった場合は、その値をそのモジュールの値として返します。見つからない場合は、関連付けられたスクリプトをロードし、実行されるまで待機してから、グローバルシンボルを見つけて返します。

覚えておくべき重要な事実は、shim構成にexports値が含まれていない限り、その構成のinitメソッドは実行されないということです。依存関係ローダーは、モジュールを初期化する前に、モジュールの値(exportsが指定するもの)を見つける必要があります。そのため、そのモジュールにシムinitがある場合、プロパティが必要です。

更新:また、問題のモジュールがdefineをどこかで呼び出した場合、そのモジュールに対して持っているシム構成はすべて無視されることを指摘する必要があります。シム構成を使用してjQueryのjQuery.noConflict(true)メソッドを呼び出してjQueryの展開を解除し、それを必要とするモジュールのみにスコープを設定したが、それを取得することができなかったため、これは実際に私にいくつかの問題を引き起こしましたワーキング。 (シム構成の代わりにマップ構成を使用してこれを簡単に行う方法については、下部の更新を参照してください。)

update 2:requireJS googleグループに関する最近の質問で、私の説明は少し誤解を招く可能性があることに気づきました。そのため、明確にしたいと思います。 requireJSは、requireJSを介して少なくとも1回ロードされた場合にのみ、シム化された依存関係を再利用します。つまり、ホスティングページにアンダースコアなどの<script>タグがある場合、次のようになります。

<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>

...そしてrequireJS設定に次のようなものが含まれています:

paths: {
    'underscore': 'lib/underscore'
},
shim: {
    'underscore': {
        exports: '_'
    }
}

次に、最初にdefine(['underscore'], function (_) {});またはvar _ = require('underscore');を実行すると、以前に定義されたwindow._を再利用するのではなく、アンダースコアライブラリを再読み込みします。前にアンダースコア。もちろん、_がルートスコープで既に定義されているかどうかを確認できますが、すでに存在する_paths構成で定義されているものと同じであることを確認する方法はありません。たとえば、prototypejqueryの両方がデフォルトでwindow.$に自分自身を割り当て、requireJSが実際にプロトタイプであるときに「window。$」がjQueryであると想定すると、状況が悪くなります。

つまり、そのようなスクリプトの読み込みスタイルを組み合わせて使用​​すると、ページは次のようになります。

 <script src='lib/underscore.js'></script>
 <script src='lib/require.js' data-main='main.js'></script>
 <script src='lib/underscore.js'></script>

2番目のアンダースコアインスタンスは、requireJSによって読み込まれたインスタンスです。

基本的に、requireJSがライブラリを認識するには、requireJSを介してライブラリをロードする必要があります。ただし、アンダースコアが必要なnext時間、requireJSは「ねえ、すでにロードしているので、exportsの値が何であれ返してください」別のスクリプトの読み込みについて心配する必要があります。」

つまり、2つの実際のオプションがあります。 1つは、私がアンチパターンと見なすものです。グローバルスクリプトの依存関係を表すためにrequireJSを使用しないでください。つまり、ライブラリがグローバルをルートコンテキストにアタッチしている限り、依存関係が明示的に要求されていない場合は、イベントにアクセスできます。これがアンチパターンである理由がわかります。基本的には、AMDローダーを使用することによる利点のほとんど(明示的な依存関係のリストと移植性)を排除しただけです。

もう1つのより良いオプションは、requireJSを使用してすべてをロードすることです。自分で作成する必要がある実際のスクリプトタグは、requireJSを最初にロードするものだけです。シムを使用することもできますが、95%の時間では、代わりにスクリプトにAMDラッパーを追加することはそれほど難しくありません。 AMD以外のすべてのライブラリをAMD互換に変換するには少し時間がかかる場合がありますが、1つまたは2つを実行すると、はるかに簡単になります。汎用のjQueryプラグインを使用して、AMDモジュールに変換できます。 1分未満で。それは通常、追加するだけの問題です

define(['jquery'], function (jQuery) {

上部、そして

    return jQuery;
});

下部にあります。 $ではなくjQueryへの 'jquery'マッピングがある理由は、最近のほとんどのプラグインが次のようなクロージャーにラップされていることに気付いたからです。

(function ($) {
    // plugin code here
})(jQuery);

そして、意図されたスコープに注意を払うことは良い考えです。ただし、プラグインが$ではなくjQueryを検出することを想定していない場合は、「jquery」を$に直接マップできます。これは基本的なAMDラッパーにすぎません。一般的に、より複雑なものは、使用されているローダーの種類(commonJS vs AMD vs通常のol 'グローバル)を検出し、結果に応じて異なる読み込み方法を使用しようとします。あなたはグーグルで数秒でこれの例をかなり簡単に見つけることができます。

更新:RequireJSでのjQuery.noConflict(true)の使用をサポートするために使用した回避策は機能しましたが、jQueryソースに非常に小さな変更を加える必要がありました。 。幸いなことに、RequireJSのドキュメントに追加したRequireJSの作者であるJames BurkeもそれをRequireJSのドキュメントに追加しました。 http://requirejs.org/docs/jquery.html#noconflictmap

22
Isochronous