見出し画像

すべてをjsにまとめる思想を理解する - webpackハンズオンシリーズ

javascriptの開発では、sassやtypescriptなどのコンパイル、minifyやautoprefixerでの最適化、依存関係を解決しbundleするなど多様な工程があるので、属人化・職人依存を避けるためにタスクランナーでの自動化が昔から当たり前に行われています。

webpackはこの手のツールのデファクトです。webpackはタスクの自動化支援ではなく、なんでもjsにまとめるという仕事をうまくやる事に特化しています。gulpやbrowserifyで行なっていたようなタスクの自動化はnpm scriptで十分やん、という割り切りを感じます。

なんでもjsで扱えるようにするので、cssや画像やhtmlもjs内にロードでき、設定が煩雑になりにくくなります。

webpackのloaderという仕組みがjsへの組み込みや最適化をうまくやってくれるのですが、どういうものか検証していきましょう。

※ webpack4系を扱います

準備

$ mkdir webpack-test && cd webpack-test
$ yarn init -y
$ yarn add webpack webpack-cli webpack-dev-server css-loader style-loader file-loader url-loader

webpackのCLIインタフェースでbuild、および開発サーバーを起動するコマンドをnpm scriptに書いておきましょう

// package.json
"scripts": {
   "build": "webpack-cli -p",
   "dev": "webpack-dev-server --open"
},
webpackは4系からwebpack-cliが新設され、コマンドラインタスクが分離されています

jsをjsに

まずは、シンプルにjsをjsにbuildしてみましょう。
webpackはwebpack.config.jsに設定を記述します。

//app.js
console.log('yeah!');
//webpack.config.js
module.exports = {
   entry: "./app.js",
   output: {
       filename: "bundle.js"
   } 
}

以下のコマンドを叩きます。

$ yarn run build

buildが終わり、dist/bundle.js が出力されたはずです。nodeコマンドで確認してみましょう。

$ node dist/bundle.js
yeah!

cssをjsに

次に、cssをjsにloadしましょう。ここで初めてloaderを扱うので、今回使うloaderを説明します。

style-loader: 動的にstyleタグをつくり、cssを突っ込みます。それをheadに差し込むことでcssが適用されます(ワイルド!)。
css-loader: css内のurl()や@importをjsのrequireに変換します。file-loaderやurl-loader(後述)と組み合わせてこれらのrequireを依存解決します。

さて、背景色を変えるシンプルなcssを当てて見ましょう。cssと適当なindex.htmlを用意します。

// app.css
body {
   background-color: aqua
}
// index.html
<!DOCTYPE html>
<html>
 <body>
   <h1>Sukisuki webpack!</h1>
   <script type="text/javascript" src="bundle.js"></script>
 </body>
</html>

エントリポイントのapp.jsで、app.cssをrequireします。style-loaderがこのrequireをstyleタグの差し込みに変換します。

// app.js
// console.log('yeah!');
require('./app.css')

webpack.config.jsのmoduleフィールドの設定を行い、このcssのrequireを依存解決しましょう

// webpack.config.js
module.exports = {
   entry: "./app.js",
   output: {
       path: __dirname + "/dist",
       filename: "bundle.js"
   },
   module: {
       rules: [
           { test: /.css$/, use: ['style-loader', 'css-loader'] }
       ]
   }
}

これにより、拡張子が.cssのファイルは、css-loader→style-loaderの順にloaderが適用されます(注: 配列のお尻から適用)

yarn run buildしたのち、webサーバーを立てブラウザから確認してみます。画面が青くなったでしょうか。

$ yarn run build

# 開発サーバーを起動
$ yarn run dev

sytle-loaderにより、styleタグが差し込まれていることがわかりますね。

sass-loaderをcss-loaderの前に差し込むことで、sassのコンパイルを行うこともできます。
実際には、事前にcssをファイルに吐き出してstyleタグで読ませたいケースが多いと思います(一般的ですね)。ExtractTextPluginを用いるとbundleしたjsから任意のファイルを抽出できます。

jpgをjsに

次に、画像をjsでloadして、body配下に追加してみることにします。

画像のような静的ファイルのロードには、file-loaderとurl-loaderが活躍します。

file-loader: ファイルをpublicな場所に保存しそのパスを返します。ファイル名にはhashが付与されキャッシュを効かせることもできます。
url-loader: base64文字列に変換して返します。指定サイズ以上の時はfile-loaderを適用するように設定できます。

適当な画像をurl-loaderで読み、createElementでつくったimgタグにセットし、動的にbodyに追加します。今回はこの画像(14kb)を使ってみましょう。

$ mkdir assets
$ cp /path/to/sample.jpg  assets/
// app.js
var img = document.createElement('img');
img.src = require('./assets/sample.jpg');
document.body.appendChild(img);

さて、今回はrequire('./assets/sample.jpg')を依存解決する必要があります。webpack.config.jsで画像ファイルに対するruleを足します。

// webpack.config.jsの一部
module: {
   rules: [
       { test: /.css$/, use: ['style-loader', 'css-loader'] },
       {
           test: /\.(png|jpe?g|gif)$/,
           use: [{
             loader: 'url-loader',
             options: {
               limit: 20000,
               name: '[name].[ext]'
             }
           }]
       }
   ]
}

これで、yarn run buildしてブラウザを見ると、画像が差し込まれているはずです。

url-loaderは静的ファイルをbase64に変換して返すloaderでした。確かにimgタグでbase64を読んでいますね。

optionsのlimitは20000になっています。これは20kバイト以上の場合はfile-loaderでの処理に切り替えるという意味です。今回のサンプル画像は20kバイト以下だったので、url-loaderで処理されました。

base64は1リクエスト内に文字列としてバイナリを組み込み転送回数を減らせるが必要以上に転送容量を食うので、特定以上のファイルサイズでは普通に別ファイルで送った方がトータルで低コストになるのです。

ためしに、limitを200にして再度buildしてみると、今度はfile-loaderで出力されたファイルを読み込んでいるのがわかります。

まとめ

・webpackは、jsへのbundleに特化させることで、cssやfontや画像やテンプレートなどを一元管理できるようにしています。

・file-loader , url-loader, style-loader, css-loaderは基本的なloaderで高確率で使うので、最初に使い方を確認するのに最適かと思います。


こんぴゅです! 外苑前から皆様に役立つテックな話題をお届けしております。もし100円でもサポいただければ励みになります。記事もグレードアップします。何卒よろしくお願いいたします