RailsにはWebpackerとか設定をいい感じにやってくれるものがありますが、 最近脱WebpackerしてWebpackをそのまま使ったり、Simpackerを導入したりするような話を耳にすることが多くなってきました。
そんな中で、Webpackが何もわかってなかったので今回は下記の記事(とてもわかりやすかった🙏✨)を参考にWebpackに入門して、いろいろイジってみたのでMEMOしておきます。
Webpackとは?
webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset. https://github.com/webpack/webpack
Webpackとはモジュールバンドラーです。jsだけでなくcssやimage等もバンドルすることが出来ます。
複数に分割されたファイルを1つにまとめることでリクエスト数を削減でき、パフォーマンスの向上が見込めます。
またpluginを用いてbabel等をあわせて実行することができるまた、フロントエンドまわりのタスクを一本化できるところも魅力のようです👀
Webpackの導入方法
導入には、まず下記のコマンドを実行します。
npm i -D webpack webpack-cli
次にバンドルするファイルを作成します。Webpackはデフォルだと下記のような設定でバンドルするので、それぞれ作成します。
項目 | 内容 |
---|---|
entry | src/index.js |
output | dist/main.js |
※entry: バンドルする対象のファイル、output: バンドル後の出力先
あとはpackage.json
のscriptsにビルド用のコマンドを追加してあげて、
"scripts": { "build": "webpack",
npm run build
で実行出来ます📦✨
$ npm run build Hash: 9ca4e2bf9b0dda113944 Version: webpack 4.42.1 Time: 190ms Built at: 2020-04-19 12:23:29 Asset Size Chunks Chunk Names main.js 952 bytes 0 [emitted] main Entrypoint main = main.js [0] ./src/index.js 131 bytes {0} [built]
Webpackをカスタマイズする
Webpackをカスタマイズするには、webpack.config.js
を作成してそこに値を記載していきます。
例えばbuildモードをdevelopment
に変更する場合は下記のようにします。
module.exports = { // ビルド時のモード // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。 // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト) mode: "development" }
他にも色々な項目が用意されているので詳しくはこちら👇
サンプル集
ちょっと個人的に色々設定をいじってみたので、サンプル集として載せておきます!
webpack-dev-serverを使う
毎回ファイルを編集したときにnpm run build
を実行するのは面倒くさいですし、全量ビルドが走るので効率がよくないです。そういう場合にはwebpack-dev-server
が便利です。(webpack --watch
でも良いですが)
npm i -D webpack-dev-server
を実行してwebpack-dev-server
をinstall
下記のようにwebpack-dev-server
用の設定をwebpack.config.js
に追加
module.exports = { // ビルド時のモード // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。 // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト) mode: "development", // webpack-dev-server用の設定 devServer: { // webpackのoutput pathを指定 contentBase: `${__dirname}/dist`, // 実行時にブラウザを開く open: true }
scriptsにも起動用のコマンドを追加して、
"scripts": { "build": "webpack", "build-dev-server": "webpack-dev-server",
npm run build-dev-server
を実行すると、Webpackビルド用のサーバーが立ち上げって自動的に差分ビルドが走るようになります🤖
ℹ 「wds」: Project is running at http://localhost:8080/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /Users/morita.jun/Documents/repo/javascript/webpack_study/dist ℹ 「wdm」: wait until bundle finished: / ℹ 「wdm」: Hash: 560347cd3ca9f479cd19 Version: webpack 4.42.1 Time: 1816ms Built at: 2020-04-19 12:33:53
ダイジェストを付与してバンドルする
webpackでバンドルしたときに毎回同じファイル名で出力してしまうとブラウザキャッシュにより、 うまく変更が反映されないといった問題が発生します。
なので出力時のファイル名にhashを付与するように設定を変更してみます。
やり方は簡単でoutput
のfilename
の中で[hash]
をつけてあげるだけです。
module.exports = { // ビルド時のモード // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。 // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト) mode: "development", // 出力設定 // デフォルトはdist/main.js output: { // 出力先のディレクトリ path: `${__dirname}/dist`, // 出力先のファイル filename: 'main-[hash].js' }, // webpack-dev-server用の設定 devServer: { // webpackのoutput pathを指定 contentBase: `${__dirname}/dist`, // 実行時にブラウザを開く open: true } }
この設定で実行するとmain-b562cb2621248a80cacb.js
のようにdigestを付与することが出来ます。
しかし、digestを付与してあげるとhtmlでスクリプトを読み込むときに毎回srcを変更しないといけないのが不便です。
そんなときは、html-webpack-plugin
が便利です。
npm install -D html-webpack-plugin
で導入して下記のように設定すると自動的にsrcを指定してくれます。
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // ビルド時のモード // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。 // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト) mode: "development", // 出力設定 // デフォルトはdist/main.js output: { // 出力先のディレクトリ path: `${__dirname}/dist`, // 出力先のファイル filename: 'main-[hash].js' }, // webpack-dev-server用の設定 devServer: { // webpackのoutput pathを指定 contentBase: `${__dirname}/dist`, // 実行時にブラウザを開く open: true }, plugins: [ // distのHTMLを自動生成するplugin、digest等をよしなに対応してくれる new HtmlWebpackPlugin(), ] }
下記のように良い感じにsrc
を設定してくれます👍
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Webpack App</title> <meta name="viewport" content="width=device-width, initial-scale=1"></head> <body> <script src="main-c6653b46c86daeb1df0d.js"></script></body> </html>
entryを複数用意してentry毎にバンドルする
デフォルトの設定だとentryもoutputも一つのファイルなので、 実際のプロジェクトだとentry/outputが肥大化して、初回のリクエストに時間が掛かる等が問題になることもあるかと思います。
webpackのentryとoutputの設定を変更することで、entryとなるファイルと出力先を複数ファイルにして、 ファイルの肥大化を抑えることが出来ます。
下記の設定では、entryをsrc/index.js
とsrc/home.js
として、そのentryファイル毎に出力するようにしています。※[name]
を使うことでentryのファイル名を取得できる。
またHtmlWebpackPlugin
のオブジェクトをentry単位に用意してそれぞれを読み込むようにしています📦
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // ビルド時のモード // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。 // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト) mode: "development", // jsのエントリーポイント // デフォルトはsrc/index.js entry: { main: './src/index.js', home: './src/home.js' }, // 出力設定 // デフォルトはdist/main.js output: { // 出力先のディレクトリ path: `${__dirname}/dist`, // 出力先のファイル filename: '[name]-[hash].js' }, // webpack-dev-server用の設定 devServer: { // webpackのoutput pathを指定 contentBase: `${__dirname}/dist`, // 実行時にブラウザを開く open: true }, plugins: [ // 個別のbuild後のjsを読み込むHTMLを生成 new HtmlWebpackPlugin({ filename: 'main.html', chunks: ['main'] }), new HtmlWebpackPlugin({ filename: 'home.html', chunks: ['home'] }) ] }
cssをバンドルする
RailsだとcssのビルドはSproketsで行うことが多いですが、Webpackでもcssのバンドルを行うことが出来ます。
Webpackでcssのビルドを行うためにはstyle-loader
とcss-loader
が必要です。
下記でinstallします。
npm -D style-loader css-loader
設定は下記のような形です。module
のrule
に適用したいloader
(今回だとstyle-loader
とcss-loader
)の設定を追加します。
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // ビルド時のモード // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。 // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト) mode: "development", // jsのエントリーポイント // デフォルトはsrc/index.js entry: { main: './src/index.js', home: './src/home.js' }, // 出力設定 // デフォルトはdist/main.js output: { // 出力先のディレクトリ path: `${__dirname}/dist`, // 出力先のファイル filename: '[name]-[hash].js' }, // webpack-dev-server用の設定 devServer: { // webpackのoutput pathを指定 contentBase: `${__dirname}/dist`, // 実行時にブラウザを開く open: true }, module: { rules: [ // stylesheetのbuild用の設定 { // 対象のファイル test: /\.css/, // 使用するloader use: [ "style-loader", "css-loader" ] } ] }, plugins: [ // 個別のbuild後のjsを読み込むHTMLを生成 new HtmlWebpackPlugin({ filename: 'main.html', chunks: ['main'] }), new HtmlWebpackPlugin({ filename: 'home.html', chunks: ['home'] }) ] }
これでcssのbundleが行われるようになりました🙌 ※実際は下記のようにjs内でimportして使うので、jsファイルとしてバンドルされ動的にhtmlに反映されます。
// src/index.js import "./styles/index.css"
バンドル時にbabelを実行する
リアルワールドではIE11といったレガシィブラウザを扱うことも多いので、babel
を通す必要があるケースが多いです。
Webpackにはbabel
用のプラグインも用意されていて比較的簡単にbabel
を実行してビルドすることが出来ます。
まずは必要なものをinstallします。
npm install -D babel-loader @babel/core @babel/preset-env
- babel-loader : Webpack用のloader
- @babel/core : babelの本体
- @babel/preset-env : Babelのプラグインを自動判定してよしなにしてくれるやつ
その後は下記のようにbabel用の設定を追加します。
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // ビルド時のモード // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。 // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト) mode: "development", // jsのエントリーポイント // デフォルトはsrc/index.js entry: { main: './src/index.js', home: './src/home.js' }, // 出力設定 // デフォルトはdist/main.js output: { // 出力先のディレクトリ path: `${__dirname}/dist`, // 出力先のファイル filename: '[name]-[hash].js' }, // webpack-dev-server用の設定 devServer: { // webpackのoutput pathを指定 contentBase: `${__dirname}/dist`, // 実行時にブラウザを開く open: true }, module: { rules: [ // stylesheetのbuild用の設定 { // 対象のファイル test: /\.css/, // 使用するloader use: [ "style-loader", "css-loader" ] }, // babelのbuild用の設定 { // 対象のファイル test: /\.m?js$/, // 対象外のファイル exclude: /(node_modules|bower_components)/, // 使用するloader use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] }, plugins: [ // 個別のbuild後のjsを読み込むHTMLを生成 new HtmlWebpackPlugin({ filename: 'main.html', chunks: ['main'] }), new HtmlWebpackPlugin({ filename: 'home.html', chunks: ['home'] }) ] }
これでbabelも合わせて実行されるようになりました🗼
おわりに
今回はwebpackを色々触ってみました。今まで触ったことがなかったので何もわからなかったのですが、単純なことをやるだけであれば既存のライブラリが充実しているので意外と設定出来そうな感じがしました(便利✨)