ちゃんと理解するWebpack5。1:webpack基礎とSass・jsのバンドル
技術スタック Javascriptwebpack

ちゃんと理解するWebpack5。1:webpack基礎とSass・jsのバンドル

2021.03.27
2021年10月30日:修正事項があっため追記しました。

こんにちはjunです。最近の自社開発では息を吸うようにNuxt.jsやVue.js、React.jsなどを使用しています。私もそれらのJSライブラリをよく使用するのですが、使いは初めて1年ほどにして「もっとJSライブラリやNode.jsの開発をしっかり理解しないと」 という場面が増えてきました。

VueやNuxt、Reactなどは特に、ライブラリ自身のチュートリアルやインストール時のセットアップが充実しすぎてwebpack.config.jsさえ見ることもなくなりました。すぐに開発できるのが強みですが、弊害として

  • 実際の細かい原理や仕組みを把握できない
  • なにか個別にカスタマイズしようとしてもわからない
  • 応用がわからない

といったことが生じています。フレームワークやライブラリは便利ですが、簡単でもいいので構成の概念を知っておくと細かいカスタマイズや環境構築ができるようになります。

VueやReactを使わないプロジェクトなんてざらにありますし、webpackの設定ができるだけでもフロントエンドの開発でできることが違ってきます。数回に分けてwebpackを使用した以下のパターンに分けて環境構築解説をしていきたいと思います。

  • webpack基礎とSass・js
  • babelの導入、画像のバンドル、複数ファイル出力
  • htmlの取り扱いとまとめ

それではまず基礎編から初めていきます。

webpackとは

まずwebpackとはについて解説します。webpackは静的モジュールバンドラーというもので、複数のJSファイルなどを1つのファイルにまとめることができます。1つにまとめることで

  • 依存性の解決(JSの読み込む順番などを気にしなくて済む)
  • モジュール化による保守性と安全性の向上
  • 圧縮による軽量化

などが見込まれます。Browserifyというものなど、他にもバンドラーはありますが今は性能的、機能的にもwebpackがかなり主流になっています。

タスクランナー的な使い方も可能であり、watchして差分だけビルドして開発するなんてことも可能です。

基礎的な概念

公式ドキュメントにもある通り、構築において以下の概念が重要となります。

  • Entry(バンドルの起点となるファイル)
  • Output(バンドルファイルの吐き出し先)
  • Loaders(JS以外のファイルを扱えるようにする)
  • Plugins(最適化したり、さらなる機能を追加する)
  • Mode(開発か本番か)

今のところ特にパッとしなくてもいいので、上記にあげた5パターンを再現するためにはこの概念が必要であること、そしてその通りに設定していることを頭に入れておいてください。

まずは複数のJSファイルやライブラリをバンドルしてみよう

まずはSassとかは忘れて、複数のJSファイルとモジュールの連携をやってみましょう。新しくディレクトリを作成してnpm initします。

npm init
npm install -D webpack webpack-cli

webpackは開発時しか使わないので -DをつけてdevDependenciesに入れます。インストール後にはnode_moduelsが作成されます。そして以下のディレクトリとファイルを作成します。

├── dist #作成
    ├── index.html #作成
├── node_modules
├── package-lock.json
├── package.json
├── webpack.config.js
└── src #作成
    ├── functions.js #作成
    └── main.js #作成

開発しているときはmain.jsfunctions.jsに記述していきます。functions.jsには共通の関数的な物を書き、モジュールとしてはおなじみのjqueryをインストールしておきます。

npm install jquery --save

index.htmlを適当に以下のようにしておきます。

dist/index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>webpackの練習</title>
    </head>

    <body>
        <main>
            <div id='app'>

            </div>
        </main>
    </body>
    <script src='./bundle.js'></script>
</html>

それでmain.jsは適当にこうしておきましょう。

src/main.js
import $ from 'jquery';

$('#app').text('hello world')

node_modules からjQueryをインポートして、index.htmlを操作します。しかし今のままではバンドルされていないので、main.jsの内容は利用できません。webpack.config.jsを設定して、main.jsをビルドしてみましょう。

webpack.config.jsを以下のように設定してみましょう。

webpack.config.js
module.exports = {
    //バンドル対象のファイル
    entry: `./src/main.js`,
  
    mode:"development",
    // ファイルの出力設定
    output: {
      //  出力ファイルのディレクトリ名
      path: `${__dirname}/dist`,
      // 出力ファイル名
      filename: "bundle.js"
    }
};

ここで先ほどの5つの概念を思い出してください。Entry、Output、Modeの設定が書かれています。パスはwebpack.config.jsから見たパスになります。エントリーはsrc配下のmain.js、吐き出し先は同じ階層のdistbundle.jsという名前で吐き出します。

module.exportsnode.jsのモジュールとして利用するために必要です。webpackはnode.jsの環境下でビルドを行うからです。

設定が終わったらpackage.jsonに以下の内容を付け足しておきます。

package.json
"scripts": {
    "build": "npx webpack-cli build", #これ
    "test": "echo \"Error: no test specified\" && exit 1"
},

npm run buildを叩くとwebpackのバンドルが走ります。それではやってみましょう。

npm run build
> webpack_parctice@1.0.0 build /Users/junjiishii/Desktop/my_apps/webpack_parctice
> webpack

asset bundle.js 323 KiB [emitted] (name: main)
runtime modules 937 bytes 4 modules
cacheable modules 282 KiB
  ./src/main.js 54 bytes [built] [code generated]
  ./node_modules/jquery/dist/jquery.js 282 KiB [built] [code generated]
webpack 5.28.0 compiled successfully in 234 ms

jqueryがきちんとnode_modulesから読み込まれいます。そしてdist配下を見るとbundle.jsができています。bundle.jsの中身をみてみますと、

わお。必要なjsファイルが一つにまとめられているので、こうなっています。main.js一行とjqueryがくっついています。それではindex.htmlをみてみると..

はい。main.jsでhello worldを入れたのできちんとmain.jsjqueryがバンドルされ、動作していることが確認されました。

自前のファイルをバンドルしよう

先程はnode_modulesjqueryをインポートして利用してみました。次はfunctions.jsの関数群をインポートして利用できるようにしてみましょう。index.htmlmain.jsfunctions.jsを以下のように変更します。

dist/index.html
<!-- body のみ見せます -->
<body>
    <main>
        <div id='app'></div>
        <input type="text" value="" id="inputs">
        <button id="submmit" >追加する</button>
    </main>
</body>
functions.js
import $ from 'jquery';

export default {
    addNewText(to,input){
        let text = $(input).val();
        $(to).append('<p>'+text+'</p>'); #XSSできちゃうので本番では使わないように。。
        $(input).val('');
    }
}
src/main.js
import $ from 'jquery';
import funcs from './functions';

$('#submmit').on('click',()=>{
    return funcs.addNewText('#app','#inputs');
})

functions.jsには便利関数を入れているのを想定しているので、オブジェクトに関数を入れておきます。それをexportします。addNewText()は指定した入力フォームの文字列を、指定したDOMにpタグとして入れてくれる神メソッドです。TODO LIST的な物を開発していると思ってください。

main.jsではimport funcs from './functions';で読み込みます。funcsというのはオブジェクトなのでfuncs.addNewText()で使用できます。イベントリスナーでボタンを押したら追加できるようにしてみましょう。

ビルドしたら再度index.htmlをみてみます。

入力内容を入れて、追加するを押すとこのように文字が追加されました。webpackが各ファイルのimportの関係性を解決してくれるので、これで複数ファイルのバンドルができるようになりました。

watchモードを追加しておく

jsのコードを書いてビルドしたらスクリプトにエラーが起きていたという時、毎回ビルドするのは面倒です。webpackにはwatchモードという物があり、変更を検知して差分ビルドをしてくれます。package.jsonに以下のように記述します。

package.json
"scripts": {
  "build": "npx webpack-cli build",
  "watch": "npx webpack-cli watch", #これ
  "test": "echo \"Error: no test specified\" && exit 1"
},

これでnpm run watch を行うことでwatchモードでビルドが動くようになります。

Sassをコンパイル・バンドルしてみる

Webpackは基本的にjsファイルのバンドルを想定していますが、css・sassもバンドルできます。今回はsassの説明をします。

必要なモジュールを追加インストール&設定

webpackがsassをバンドルしてビルドするには

  • sass-loader
  • node-sass
  • css-loader
  • mini-css-extract-plugin

の4つのdevDependenciesが必要になります。

sass-loaderは重要な概念に出てきた「loader」に該当します。webpackがsassファイルをバンドルするのに必要です。node-sassはnode.jsでsassのコンパイルをするために必要です。sassをコンパイルして生成されたcssを扱うためにcss-loader、mini-css-extract-pluginが必要です。そのためまずはインストールしてみましょう。

npm install -D sass-loader node-sass css-loader mini-css-extract-plugin

ひとまず入れられたら、webpack.config.jsを以下のように変更します。

webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    //バンドル対象のファイル
    entry: './src/js/main.js',
  
    mode:"development",
    
    // ここから追加
    module: {
        rules: [
          {
            test: /\.(sa|sc|c)ss$/,
            exclude: /node_modules/,
            use: [
              MiniCssExtractPlugin.loader,
              {
                loader: 'css-loader',
                options: { url: false }
              },
              'sass-loader'
            ]
          }
        ]
      },
    // ファイルの出力設定
    output: {
      //  出力ファイルのディレクトリ名
      path: `${__dirname}/dist`,
      // 出力ファイル名
      filename: "bundle.js"
    },
   // ここも追加
    plugins: [
      new MiniCssExtractPlugin({
          filename:'style.css'
      })
    ]
};

アセットのディレクトリとindex.htmlの変更

srcにsassのディレクトリを作成し、そしてややこしいのでjsのフォルダも作りました。

.
├── dist
│   ├── index.html
├── package-lock.json
├── package.json
├── src
│   ├── js
│   │   ├── functions.js
│   │   └── main.js
│   └── sass
│       ├── component.scss
│       ├── style.scss
│       └── variable.scss
└── webpack.config.js

sassの3ファイルは以下のような構成になっています。

variacle.scss
$base_color:red;
$box_size:30px;
component.scss
.box{
    background-color: $base_color;
    width: 20px;
    height: 20px;
}
style.scss
@import './variable.scss';
@import './component.scss';

index.htmlもバンドルされたstyle.cssを読み込むようにしましょう。

dist/indexx.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>webpackの練習</title>
     <!-- これ -->
        <link rel="stylesheet" href="./style.css">
    </head>

    <body>
        <main>
            <div id='app'>

            </div>
            <input type="text" value="" id="inputs">
            <button id="submmit" >追加する</button>
            <div class='box'></div>
        </main>
    </body>
    <script src='./bundle.js'></script>
</html>

そしてエントリーファイルであった./src/js/main.jsに以下のようにscssをインポートします。

src/js/main.js
import $ from 'jquery';
import funcs from './functions';
import '~/sass/style.scss';

$('#submmit').on('click',()=>{
    return funcs.addNewText('#app','#inputs');
})

こうするとwebpackが~/sass/style.scssのインポートを捉え、適切にバンドルしてくれます。

それぞれのモジュールの説明

webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
...
module: {
    rules: [
        {
        test: /\.(sa|sc|c)ss$/,
        exclude: /node_modules/,
        use: [
            MiniCssExtractPlugin.loader,
            {
            loader: 'css-loader',
            options: { url: false }
            },
            'sass-loader'
        ]
        }
    ]
},
...
plugins: [
    new MiniCssExtractPlugin({
      filename: 'style.css'
    })
]

webpack.config.jsでは特にこの箇所が重要になります。moduleの中身でsassや画像などのファイルを取り扱えるようになります。rulesという箇所に取り扱うファイルの拡張子を正規表現で捉え、それに対するローダーの使用などを定義します。

MiniCssExtractPluginはcssをstyle.cssのような外部ファイルとして出力するために必要なプラグインです。main.jsにてimport '~/sass/style.scss';という記述があったと思います。このプラグインを使わない場合、webpackでのcssバンドルは外部ファイルでなく、HTMLに直接インライン記述をしようとします。(そうした方が処理と通信が早くなるらしい)

しかし外部ファイルstyle.cssとして今回は出力したいので、MiniCssExtractPluginを用いてmain.jsimport '~/sass/style.scss';のインポートを参考に、dist配下にstyle.cssを出力します。ように外部ファイルとして出力します。MiniCssExtractPluginのローダーとプラグインに記述しておきます。プラグインの箇所では出力ファイル名を指定します。filename: 'style.css'としていますが、指定しない場合はエントリーファイルのmain.jsからmain.cssが出力されます。

以上でsassのバンドルができるようになりました。早速やってみます。

npm run build

するとdistにstyle.cssができました。中身をみてみるとsassで定義した依存性や変数が当てはまっています。

基礎編終了

以上がwebpackを用いたjsファイルのバンドルとsassのコンパイルです。大切なのは5つの概念と必要なモジュールを読み込み、設定することです。次回は忌まわしきIEでjsが動くようにすること、画像の依存性解決、複数条件のバンドルを解説します。

コメント

コメント読み込み中..

Copyright © 2021 jun. All rights reserved.