9
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[JavaScript] Webpackとはなんぞや。そして、Slick CarouselをWebpackで使う

Posted at

ストーリー

Webpack便利ですね。
複数のNodeモジュールをさくっとひとまとめにしてくれます。
各種ローダーを入れれば、ECMAScriptを変換したり、CSSも読み込んだりできます。
minifiedもできちゃいます。

どんなものなのか理解しないまま、日々使っていたのですが、ここに来てjQueryプラグインを使う必要が出てきました。
slick-carousel
です。

いつものように読み込んだところ、

Uncaught TypeError: t(...).slick is not a function

と、何やら不穏な動き。

重い腰を上げて、Webpackが何をしてくれるものなのかを理解することにしたのでした。

導入

シンプルな環境で試してみます。

mkdir webpack-exp
cd webpack-exp
npm init
npm install --save-dev webpack

これで、node_modules/.bin/webpackコマンドが使えるようになりました。

設定ファイル

webpackの設定ファイルを作成します。

webpack.config.js
const path = require('path');

module.exports = {
    entry: './src/app.js',                                                                
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
};

空っぽのソースはどうなる?

まずは空っぽのソースを作ってwebpackしてみました。

src/app.js
/* I am app.js */
dist/bundle.js
(function(modules) {
     /*
      *
      * (省略) Webpack初期化コード
      * 
      */
})([
    (function(module, exports) {
        /* I am app.js */
    })
]);

app.jsの内容が、moduleexportsを引数とする関数でラッピングされています。
読み込んだソース中でmoduleexportsが使えるのはこういうわけでしたか。

ラッピングされた関数は、webpack初期化コードに渡されて順番に呼び出されています。

jQueryを読み込んでみる

試してみたかったjQueryを読み込んでみます。

npm install --save-dev jquery

ソースは次のようにしてみました。

src/app.js
import jQuery from 'jquery';

/* I am app.js */
dist/bundle.js
(function(modules) {
     /*
      *
      * (省略) Webpack初期化コード
      * 
      */
})([
    (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";

        Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

        var __WEBPACK_IMPORTED_MODULE_0_jquery__ =
          __webpack_require__(1);

        var __WEBPACK_IMPORTED_MODULE_0_jquery___default =
          __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_jquery__);

        /* I am app.js */

    }),
    (function(module, exports, __webpack_require__) {
        var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;

        /*
         * (省略) jQuery本体のコード
         */
    })
]);

importに書いたjQueryはどこにいっちゃいましたかね?

あと、ラッピング関数の引数が変わってますね。exportsがなくなっちゃいました。

jQueryを使ってみる

中身のあるソースにしてみました。

src/app.js
import jQuery from 'jquery';                                                                                                                                                        

/* I am app.js */
;(function($) {
    $(function() {
        console.log('Hello I am app.js');                                                                                                                                           
    });
})(jQuery);
dist/bundle.js
(function(modules) {
     /* (snip) */
})([
    (function(module, __webpack_exports__, __webpack_require__) {
        (snip)
        var __WEBPACK_IMPORTED_MODULE_0_jquery___default =
          __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_jquery__);

        /* I am app.js */
        ;(function($) {
            $(function() {
                console.log('Hello I am app.js');
            });
        })(__WEBPACK_IMPORTED_MODULE_0_jquery___default.a);
    }),
    (function(module, exports, __webpack_require__) {
        (snip)
    })
]);

元ソースにあったjQueryが、いい感じに置き換えられています。
ブラウザで読み込めば、ちゃんと動きますよー。

importrequireって違うのか?

ところで、外部モジュール読み込みに使うrequireは、importと違うのかどうか?

src/app.js
const jQuery = require('jquery');                                                                                                                                                   

/* I am app.js */
;(function($) {
    $(function() {
        console.log('Hello, I am app.js');
    });
})(jQuery);

ラッピングする関数は、このようになりました。

dist/bundle.js
function(module, exports, __webpack_require__) {
    const jQuery = __webpack_require__(1);

    /* I am app.js */
    ;(function($) {
        $(function() {
            console.log('Hello, I am app.js');
        });
    })(jQuery);
}

importよりシンプルな中身になりました。

jQueryプラグインを使ってみる

npmで入るなにか簡単なプラグインということでjquery.marqueeを試してみます。

npm install --save-dev jquery.marquee
src/app.js
const jQuery = require('jquery');
require('jquery.marquee');

/* I am app.js */
;(function($) {
    $(function() {
        $('.marquee').marquee({                                                                                                                                                     
            duration: 5000                                                                                                                                                          
        });     
    });
})(jQuery);

非情なエラー

ブラウザで読み込んだところ、エラーになってしまいました。

bundle.js:10828 Uncaught ReferenceError: jQuery is not defined
    at Object.<anonymous> (bundle.js:10828)
    at __webpack_require__ (bundle.js:20)
    at Object.<anonymous> (bundle.js:71)
    at __webpack_require__ (bundle.js:20)
    at bundle.js:63
    at bundle.js:66

非情な原因

jquery.marqueeのソースを眺めてみると、window.jQueryではなく、jQueryを参照しているのが原因のようです。むむむ。。。

温厚(?)な解決策

jQueryをglobalオブジェクトに入れてあげればいいようです。

src/app.js
global.jQuery = require('jquery');

ラッピング関数部分が、このように変わりました。

dist/bundle.js
function(module, exports, __webpack_require__) {
    (function(global) {
        global.jQuery = __webpack_require__(2);
        __webpack_require__(3);

        /* I am app.js */
        ;(function($) {
            $(function() {
                $('.marquee').marquee({
                    duration: 5000
                });
            });
        })(jQuery);
    }.call(exports, __webpack_require__(1)))
}

__webpack_require__(1)で返される関数の中でグローバルなオブジェクトを定義しています。

dist/bundle.js
function(module, exports) {
    var g;

    // This works in non-strict mode
    g = (function() {
        return this;
    })();

    try {
        // This works if eval is allowed (see CSP)
        g = g || Function("return this")() || (1,eval)("this");
    } catch(e) {
        // This works if the window reference is available
        if(typeof window === "object")
                g = window;
    }

    // g can still be undefined, but nothing to do about it...
    // We return undefined, instead of nothing here, so it's
    // easier to handle this case. if(!global) { ...}
    module.exports = g;
}

なるほどねー。

さて、Slick Carousel

カルーセル、、、(オヤジギャグ審議中)

インストールします。

npm install --save-dev slick-carousel
src/app.js
const jQuery = require('jquery');
require('slick-carousel');

/* I am app.js */
;(function($) {
    $(function() {
        $('.myslick').slick();
    });
})(jQuery);

再現しないエラー

・・・なんか動いてしまいました。いや、動いていいんですけどね。

元エラーの原因

jQueryをscriptタグでもも読み込んでた。。。

まとめ

Webpackは読み込んだモジュールをいい塩梅に関数内に分離してくれて、必要があればそれを各モジュールに渡してくれることがわかりました。

さらに得られた知見としては、

  • globalオブジェクトを恐れず使え
  • jQueryを何度も読み込まない orz

というところでしょうか。

環境

最後に試した環境です。

  • node 8.6.0
  • npm 5.3.0
  • webpack 3.8.1
  • jquery 3.2.1
  • slick-carousel 1.8.1
9
12
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
9
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?