web-dev-qa-db-ja.com

Webpack:angular jQueryを自動検出し、jqLit​​eの代わりにangle.elementとして使用する方法は?

Webpackを使用してAngular 1.4プロジェクトをビルドしています。このプロジェクトは、angularディレクティブにラップされたいくつかのjQueryプラグインを使用します。これらのディレクティブは内部的にangular.elementを使用します。これはおそらく、angle.elementがjqLit​​eではなく実際のjQueryであることを意味します。

angularでjQueryを自動検出し、jqLit​​eの代わりに使用するようにします。エントリポイントモジュールapp.jsrequire('jquery')でローカルにjqueryを要求し、require(expose?$!expose?jQuery!jquery)でjQueryをグローバルに公開しようとしました。

それでも、私が何をするにしても、angular.elementはjqLit​​eを指します。


私の研究の結果、いくつかの発見がありました。

  1. CommonJSモジュールとしてインポートされた場合でも、Angularはグローバル変数window.angularに自身を割り当てるため、Webpackでexposeする必要はありません。 Does Angular CommonJSモジュールとしてロードされた場合、自身をグローバルに `window.angular`に割り当てますか?
  2. ProviderPluginはトリックを実行していないようです。jQueryをグローバル名前空間に公開しません。代わりに、グローバル名jQueryに依存するすべてのモジュールに対して、require('jquery')を挿入します。私は100%確信していませんが、Angularはグローバル名前空間からjQueryに直接アクセスしないように見えますが、代わりにbindJQuery関数でwindow.jQueryにアクセスしようとするので、このアプローチは役に立ちません: WebpackでjQueryを実際のWindowオブジェクトに公開
  3. ProviderPluginと同じ理由で、imports-loaderは不適切と思われます。Angularは、jQueryだけでなく、window.jQueryを必要とします。
  4. expose-loaderを使用すると、jqueryはウィンドウオブジェクトになります。私の問題は、Babelがすべてのインポートをモジュールの最上部に引き上げることでした結果のコード。したがって、ソースファイルではrequire(expose?jquery!jquery)import angular from "angular"の前にありましたが、バンドルではrequire("angular")がファイルの先頭、jqueryの前にあるため、Angularがインポートされるまでに、jqueryはまだ利用できません。私は、ECMA6インポート構文でWebpackローダーを使用する方法を疑問に思います。
  5. Jqueryでimport構文の代わりにrequire構文を使用することが提案されました:require(jquery)ではなくimport "jquery"またはimport $ from "jquery":(Petr Averyanov: Webpackローダー構文の使用方法/ exports/expose)ECMAScript 6インポート? )。 jqueryソースコードは 特別なラッパーでラップ で、jqueryの必要性を特定します(AMD/require、CommonJS、または<script>ステートメントでグローバルに)。それに基づいて、jqueryファブリックに特別な引数noGlobalを設定し、noGlobalの値に基づいてwindow.jQueryを作成するかどうかを決定します。 jquery 2.2.4の時点では、import "jquery"noGlobal === trueおよびwindow.jQueryは作成されていません。 IIRC、jqueryの一部の古いバージョンは、importをCommonJSインポートとして認識せず、imported jqueryをグローバル名前空間に追加し、angularが使用できるようにしました。

詳細:ここに私のapp.jsがあります:

'use strict';

require("expose?$!expose?jQuery!jquery");
require("metisMenu/dist/metisMenu");
require("expose?_!lodash");
require("expose?angular!angular");

import angular from "angular";
import "angular-animate";
import "angular-messages";
import "angular-resource";
import "angular-sanitize";
import "angular-ui-router";
import "bootstrap/dist/css/bootstrap.css";
import "font-awesome/css/font-awesome.css";
import "angular-bootstrap";

require("../assets/styles/style.scss");
require("../assets/fonts/pe-icon-7-stroke/css/pe-icon-7-stroke.css");

// Import all html files to put them in $templateCache
// If you need to use lazy loading, you will probably need
// to remove these two lines and explicitly require htmls
const templates = require.context(__dirname, true, /\.html$/);
templates.keys().forEach(templates);

import HomeModule from "home/home.module";
import UniverseDirectives from "../components/directives";

angular.module("Universe", [
    "ngAnimate",
    "ngMessages",
    "ngResource",
    "ngSanitize",
    "ui.router",
    "ui.bootstrap",

    HomeModule.name,
    UniverseDirectives.name,
])
.config(function($urlRouterProvider, $locationProvider, $stateProvider){
    // $urlRouterProvider.otherwise('/');

    // $locationProvider.html5Mode(true);

    $stateProvider
      .state('test', {
        url: "/test",
        template: "This is a test"
      });
});
39
Boris Burkov

john-reilly からこの回答を得ました:
webpack angular and jquery)の神秘的なケース

bob-sponge の答えは正しくありません-Provideプラグインは、処理するモジュールで実際にテキスト置換を行っているため、window.jQuery(angularが探しているもの)であり、jQueryだけではありません。

あなたのwebpack.config.jsプラグインに次のエントリを追加する必要があります。

new webpack.ProvidePlugin({
    "window.jQuery": "jquery"
}),

これはwebpack ProvidePlugin を使用し、webpackification(©2016 John Reilly)の時点で、window.jQueryへのコード内のすべての参照は、jQueryを含むwebpackモジュールへの参照に置き換えられます。バンドルされたファイルを見ると、windowjQueryオブジェクトをチェックするコードが次のようになっていることがわかります。

jQuery = isUndefined(jqName) ?
  __webpack_provided_window_dot_jQuery : // use jQuery (if present)
    !jqName ? undefined : // use jqLite
    window[jqName]; // use jQuery specified by `ngJq`

そのとおり; webpackは、jQueryでAngularを提供しながら、まだnotjQuery変数をwindow。きちんとした?

43
studds

!!更新

ES6の例では、angularに必要なcommonJsを使用する必要があるようです。

import $ from "jquery"

window.$ = $;
window.jQuery = $;

var angular = require("angular");

以下は元の答えです



より簡単なソリューションを目指したいです。 angularが認識できるように、jQueryをウィンドウ全体に設定するだけです。

var $ = require("jquery")

window.$ = $;
window.jQuery = $;

var angular = require("angular");

またはあなたの場合(OUT DATED):

import $ from "jquery"

window.$ = $;
window.jQuery = $;

import angular from "angular";

これが役立つことを願っています:)

7
Lukas Chen

あなたのケースでは ProvidePlugin を使用したほうが良いでしょう。プラグインセクションのwebpack設定に次の行を追加するだけで、jqueryがアプリで使用可能になります。

    new webpack.ProvidePlugin({
         "$": "jquery",
         "jQuery": "jquery"
    })
3
Bob Sponge

そこで、実際に@BobSpongeの回答と@Bobのヒント/コメントをマッシュアップしたソリューションを提供します。だからオリジナルではなく、(Babel/ES6、BTWを使用していないプロジェクトで)私のために働くものを示し、それがなぜ働くのかを説明しようとしています...

(最後の)トリックは、実際に エクスポーズローダー を使用することです。

彼らのページで説明したように、module.loaders

{ test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },

さらに、pluginsリストには、次のものがあります。

        new webpack.ProvidePlugin(
        {
            $: 'jquery',
            jQuery: 'jquery',
            _: 'lodash',
            // [...] some other libraries that put themselves in the global scope.
            //angular: 'angular', // No, I prefer to require it everywhere explicitly.
        }),

コード内でこれらの変数のグローバルな出現を実際に見つけ、各モジュールにローカルな変数にそれらを必要とします。私がするように、(RequireJSからWebpackへの)既存のプロジェクトの移行を容易にします。

そして、重要なことは(と思う)、アプリケーションのエントリポイントで、必要な順序でそれらを必要とします。私の場合、ベンダーバンドルを作成したので、これがこのバンドルの順序です。

require('jquery');

require('lodash');
// [...]

var angular = require('angular');
// Use the angular variable to declare the app module, etc.

Webpackはrequiresを見る順序でライブラリを関連するバンドルに追加します(プラグイン/ローダーを使用してそれらを並べ替える場合を除く)。ただし、インポートは分離されているため(グローバルリークはありません)、AngularはjQueryライブラリを表示できませんでした。したがって、exposeが必要です(window.jQuery = require('jquery');代わりに、動作しませんでした。おそらく手遅れです。)

2
PhiLho

この日本語の記事があります webpackでjQLiteではなくjQueryを使用したい+ AngularJS 同じ問題について話しているようです(日本語はまだわかりません)。私はグーグルを使って英語に翻訳しました。クレジットは cither に送られます。

彼はこれを解決する4つの方法を提供します。

  1. ウィンドウに直接割り当てます(実際にはクールではありません)

    window.jQuery = require('jquery');
    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  2. expose-loader を使用します(OK、しかしそれほどクールではありません)

    npm install --saveDev expose-loader
    

    webpack.config.js

    module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        module: {
            loaders: [{
                test: /\/jquery.js$/,
                loader: "expose?jQuery"
            }]
        }
    };
    

    使用法:

    require('jquery');
    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  3. expose-loader (より良い)を使用します

    npm install --saveDev expose-loader
    

    webpack.config.js

        module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        module: {
            loaders: [{
                test: /\/angular\.js$/,
                loader: "imports?jQuery=jquery"
            }, {
                test: /\/jquery.js$/,
                loader: "expose?jQuery"
            }]
        }
    };
    

    使用法:

    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  4. ProvidePlugin (最適なソリューション)を使用します

    これは実際には スタッズの受け入れられた答え ここと同じです

    module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        plugins: [
            new webpack.ProvidePlugin({
                "window.jQuery": "jquery"
            })
        ],
    };
    

    使用法:

    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    

私たちがまったく同じ問題を抱えていたので、ここでこれを共有すると思いました。プロジェクトでexpose-loaderソリューションを使用して成功しました。 jqueryをProvidePluginに直接注入するwindowも良い考えだと思います。

2
GabLeRoux