こんにちは。KOUKIです。
最近webpackを学習したので、そのまとめをします。
<目次>
webpackとは
webpackは、Node.js上で動く静的ファイル(モジュール)のバンドラーです。
JavaScript, CSS, SCSS, PNGファイルなどの情報を一つのJavaScriptファイルの纏めてくれるツールです。
フロントエンド開発では最もよく使われます。
環境
webpackを使用するには、Node.jsをインストールしておく必要があります。
私の環境では、以下のバージョンで動作しています。
1 2 3 4 5 6 7 8 |
$ node -v v12.13.0 $ npm -v 6.12.0 $ npx -v 6.12.0 |
プロジェクトの作成
Desktop上にlearning-webpackフォルダを作成し、そこで学んでいきましょう。
1 2 |
make learning-webpack cd learning-webpack |
初期化(npm init)
最初に初期化を行います。
1 |
npm init -y |
npm initに成功するとフォルダは以下にpackage.jsonが作成されます。
package.jsonはjson形式で記載されたインストールしたパッケージを管理するためのファイルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// package.json { "name": "learning-webpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } |
このファイルは、パッケージのバージョン管理をしたり、プロジェクト起動時に実行したいコマンドを指定できるなど、様々な使い方ができます。
これから様々なパッケージをインストールしますが、必ず「package.jsonがカレントディレクトリに存在すること」を確認してからコマンドを実行してください。
webpackのインストール
webpackをインストールします。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 最新のバージョン npm info webpack webpack@4.41.2 | MIT | deps: 23 | versions: 623 npm install --save-dev webpack@4.41.2 # webpackコマンドを実行するためのパッケージ npm info webpack-cli webpack-cli@3.3.10 | MIT | deps: 11 | versions: 89 npm install --save-dev webpack-cli@3.3.10 |
live-serverのインストール
live-serverパッケージをインストールするとプロジェクト配下のファイルを監視し、変更があった場合、ブラウザ上にその変更内容を即時に反映してくれるようになります。
1 2 3 4 5 |
npm info live-server live-server@1.2.1 | MIT | deps: 13 | versions: 23 npm install --save-dev live-server@1.2.1 |
プロジェクト配下にindex.htmlを作成しましょう。
1 |
touch index.html |
ファイルの中身は、以下になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Hi, webpack!</title> <script src="https://unpkg.com/lodash@4.17.15/lodash.min.js"></script> </head> <body> Hi, webpack!! <script src="src/index.js"></script> </body> </html> |
上記には、lodashが入っていますが、今は使いません。
terminal上で以下のコマンドを実行してください。
1 |
npx live-server |
このコマンドを実行すると自動的にブラウザが立ち上がり、index.htmlの中身を表示してくれます。
webpackを利用する
webpackは、js,css,scssなどの静的ファイルを”一つのJavaScriptファイルに収めてしまう方法です。
さっそくその力を使ってみましょう。
まずは、webpackを利用していない状態で、挙動確認を行います。
1 2 3 4 5 6 7 8 9 10 11 |
mkdir dist mv index.html dist/ mkdir src touch index.js npm info lodash lodash@4.17.15 | MIT | deps: none | versions: 108 npm install --save lodash@4.17.15 |
index.jsを作成したので、中身を書いていきます。
1 2 3 4 5 6 7 8 9 |
// src/index.js function component() { const element = document.createElement('div'); const array = ['Hello', 'webpack'] element.innerHTML = _.join(array, ' ') return element; } document.body.appendChild(component()) |
このソースファイル中には、lodashを使っています。
lodashを使うと、配列の処理がびっくりするほど簡単に書けます。
画面を表示してみましょう。
1 |
npx live-server |
lodashで処理した文字列が画面上に表示されたことが分かったと思います。
次にwebpackを利用してみます。
webpackは、webpack.config.jsに設定を書き込みます。
1 |
touch webpack.config.js |
このファイルには色々な設定を書き込むことになりますが、ひとまずwebpackのバンドル対象、出力ファイル名、出力場所を記述しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// webpack.config.js const path= require('path') // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 } } |
ここまで出来たら、以下のコマンドを実行してください。
1 2 3 4 5 |
# configファイルを指定 npx webpack --config webpack.config.js # modeオプション("development", "production", "none") npx webpack --mode development |
distフォルダは以下を確認するとmain.jsファイルが作成されていることが分かると思います。
このmain.jsには、index.jsの情報はもちろん、loaderのモジュール情報も格納されています。
index.jsを以下のように修正しましょう.
1 2 3 4 5 6 7 8 9 10 11 |
// src/index.js // lodashを読み込み import _ from 'lodash' function component() { const element = document.createElement('div'); const array = ['Hello', 'webpack'] element.innerHTML = _.join(array, ' ') return element; } document.body.appendChild(component()) |
index.htmlを以下のように修正しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!-- dist/index.html --> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Hi, webpack!</title> <!-- lodash削除 --> </head> <body> Hi, webpack!! <script src="main.js"></script> <!-- main.jsに変更 --> </body> </html> |
live-serverを起動して、画面を確認してみましょう。
1 |
npx live-server |
webpack-dev-serverの導入
live-serverと同じことができるwebpack-dev-serverを導入しましょう。
1 2 3 4 5 6 |
# Webpackによるバンドル、http://localhost:8080/をリッスンする npm info webpack-dev-server webpack-dev-server@3.9.0 | MIT | deps: 33 | versions: 164 npm install --save-dev webpack-dev-server@3.9.0 |
webpack-dev-serverを起動するコマンドは、以下です。
1 |
npx webpack-dev-server --open |
このコマンドをpackage.jsonに組み込みましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
{ "name": "learning-webpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack-dev-server --open --mode development" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "live-server": "^1.2.1", "webpack": "^4.41.2", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.9.0" }, "dependencies": { "lodash": "^4.17.15" } } |
ついでに、デフォルトルートを指定し、dist配下のindex.htmlが表示されるようにしてしまいましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// webpack.config.js const path= require('path') // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 } } |
devServerが追加した設定です。
これからは、以下のコマンドで起動が可能です。
1 |
npm run start |
モジュールについて
Node.jsにおけるモジュールについて少し確認しておきましょう。
webpack.config.jsでは、次の記述をしました。
1 2 3 |
module.exports = { ... } |
このmoduleとは一体?という感じですよね?
このmoduleは、JavaScriptファイル(webpack.config.js)自身を指しており、この中に設定された機能を外部で使えるようにしています。
次のファイルを作成してください。
1 |
touch src/utilities.js |
中身は次のように書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// src/utilities.js export function PlusPlus(num) { return num + num; } export const NAME = 'Harry' export default class Magical { static say() { return 'Year!!!!!!!!!!!!!!!!!' } } |
index.jsを更新します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// src/index.js import _ from 'lodash' // パターン1 // import { NAME, PlusPlus } from './utilities' // console.log(PlusPlus(2)) // console.log(NAME) // パターン2 // import * as utilities from './utilities' // console.log(utilities.PlusPlus(2)) // console.log(utilities.NAME // パターン3 import {NAME as NAME_OF_HARRY, PlusPlus as PLUSPLUS_DESU} from './utilities' import Magical from './utilities' console.log(NAME_OF_HARRY) console.log(PLUSPLUS_DESU(2)) console.log(Magical.say()) function component() { const element = document.createElement('div'); const array = ['Hello', 'webpack'] element.innerHTML = _.join(array, ' ') return element; } document.body.appendChild(component()) |
サーバーを起動して、コンソールを確認してみましょう。
1 2 3 4 5 |
Harry index.js:23 4 index.js:24 Year!!!!!!!!!!!!!!!!! client:52 |
module.exportsを利用するともう少し構造化できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// src/utilities.js function PlusPlus(num) { return num + num; } const NAME = 'Harry' class Magical { static say() { return 'Year!!!!!!!!!!!!!!!!!' } } module.exports = { NAME: NAME, Magical: Magical, PlusPlus: PlusPlus } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// src/index.js import _ from 'lodash' import utilities from './utilities' console.log(utilities.NAME) console.log(utilities.PlusPlus(2)) console.log(utilities.Magical.say()) function component() { const element = document.createElement('div'); const array = ['Hello', 'webpack'] element.innerHTML = _.join(array, ' ') return element; } document.body.appendChild(component()) |
css-loaderとstyle-loaderを導入
css-loaderは、CSSをjavascriptと扱うローダーで、style-loaderは、htmlファイルにstyleを適用させるローダーです。
この二つのローダーを適用しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
npm info css-loader css-loader@3.2.0 | MIT | deps: 12 | versions: 94 npm install --save-dev css-loader@3.2.0 npm info style-loader style-loader@1.0.0 | MIT | deps: 2 | versions: 55 npm install --save-dev style-loader@1.0.0 |
次にwebpack.config.jsにローダーを設定するわけですが、一つ注意点があります。
1 |
Loaders can be chained. Each loader in the chain applies transformations to the processed resource. A chain is executed in reverse order. The first loader passes its result (resource with applied transformations) to the next one, and so forth. Finally, webpack expects JavaScript to be returned by the last loader in the chain. |
「A chain is executed in reverse order.」と記述が重要なのですが、設定ファイルに記述したローダーは、逆順で実行されるそうです。
以下の設定だとcss-loader -> style-loaderの順で実行されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// webpack.config.js const path= require('path') // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', // 2番目に実行 'css-loader' // 1番目に実行 ] } ] }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 } } |
通常は、css-loaderでcssをJavaScript化してからStyleを適用させたいはずなので、この順番になっていないとエラーになります。
次は、cssファイルを作りましょう。
1 |
touch src/style.css |
1 2 3 4 |
/* src/style.css*/ .haikei { background-color: #8dd6f9; } |
次は、index.jsを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 |
// src/index.js import _ from 'lodash' import './style.css' function component() { const element = document.createElement('div'); const array = ['Hello', 'webpack'] element.innerHTML = _.join(array, ' ') return element; } document.body.appendChild(component()) document.body.classList.add('haikei') |
サーバーを起動してみましょう。CSSが適用された画面が見えるはずです。
1 |
npm run start |
url-loaderとfile-loaderの導入
CSS同様に画像ファイルもwebpackageで扱えるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 |
npm info url-loader url-loader@2.3.0 | MIT | deps: 3 | versions: 28 npm install --save-dev url-loader@2.3.0 npm info file-loader file-loader@4.3.0 | MIT | deps: 2 | versions: 44 npm install --save-dev file-loader@4.3.0 |
webpack.config.jsに以下の設定ファイルを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// webpack.config.js const path= require('path') // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', // 2番目に実行 'css-loader' // 1番目に実行 ] }, { test: /\.(jpe?g|png|gif|svg|ico)$/i,// i -> 大文字小文字むし loader: 'url-loader', options: { limit: 2048, name: './images/[name].[ext]' // 2KBを超える画像ファイルは、imagesフォルダ配下に分離して配信 }, } ] }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 } } |
srcフォルダ配下に適当な画像ファイルを格納してください。
1 |
src/logo.svg |
次は、index.jsを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// src/index.js import _ from 'lodash' import './style.css' import logo from './logo.svg' function component() { const element = document.createElement('div'); const array = ['Hello', 'webpack'] element.innerHTML = _.join(array, ' ') return element; } document.body.appendChild(component()) document.body.classList.add('haikei') // Image Objectを作成 const image = new Image() // logo.svg情報を格納 image.src = logo document.body.appendChild(image) |
以下のコマンドで、サーバーを起動してください。
1 |
npm run start |
sass-loader の導入
続いて、sass-loaderを導入します。
1 2 3 4 5 6 7 8 9 10 11 |
npm info sass-loader sass-loader@8.0.0 | MIT | deps: 5 | versions: 52 npm install --save-dev sass-loader@8.0.0 0 npm info node-sass node-sass@4.13.0 | MIT | deps: 17 | versions: 136 npm install --save-dev node-sass@4.13.0 |
webpack.config.jsの設定に以下を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
// webpack.config.js const path= require('path') // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', // 2番目に実行 'css-loader' // 1番目に実行 ] }, // new { test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader' ] }, { test: /\.(jpe?g|png|gif|svg|ico)$/i,// i -> 大文字小文字むし loader: 'url-loader', options: { limit: 2048, name: './images/[name].[ext]' // 2KBを超える画像ファイルは、imagesフォルダ配下に分離して配信 }, } ] }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 } } |
CSSと設定が重複していますが、あとで修正します。
ひとまず、scssファイルを作成しましょう。
1 |
touch src/style.scss |
1 2 3 4 5 6 |
// src/style.scss $primary-color: tomato; div { color: $primary-color; } |
index.jsも修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// src/index.js import _ from 'lodash' import './style.css' import './style.scss' // new import logo from './logo.svg' function component() { const element = document.createElement('div'); const array = ['Hello', 'webpack'] element.innerHTML = _.join(array, ' ') return element; } document.body.appendChild(component()) document.body.classList.add('haikei') // Image Objectを作成 const image = new Image() // logo.svg情報を格納 image.src = logo document.body.appendChild(image) |
サーバーを立ち上げるため、次のコマンドを実行してください。
1 |
npm run start |
babelとReact環境の構築
Babelを用いるとモダンなJS記法をどのようなブラウザでも解釈できるような形に変換できます。
JavaScriptには、アロー関数など新しい記法がどんどん出てきていますが、ブラウザによっては対応していないものもあります。
<最新の記法>
1 |
const kansu = arg => arg; |
1 2 3 |
var kansu = function kansu(arg) { return arg; } |
では、さっそくパッケージをインストールします。
※installの参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
npm info babel-loader babel-loader@8.0.6 | MIT | deps: 4 | versions: 63 npm install --save-dev babel-loader@8.0.6 npm info @babel/core @babel/core@7.7.4 | MIT | deps: 14 | versions: 59 npm install --save-dev @babel/core@7.7.4 npm info @babel/preset-env @babel/preset-env@7.7.4 | MIT | deps: 51 | versions: 59 npm install --save-dev @babel/preset-env@7.7.4 npm info @babel/preset-react @babel/preset-react@7.7.4 | MIT | deps: 5 | versions: 37 npm install --save-dev @babel/preset-react@7.7.4 npm info react react@16.12.0 | MIT | deps: 3 | versions: 245 npm install react@16.12.0 react-dom@16.12.0 npm info html-loader html-loader@0.5.5 | MIT | deps: 5 | versions: 16 npm install --save-dev html-loader@0.5.5 npm info html-webpack-plugin html-webpack-plugin@3.2.0 | MIT | deps: 7 | versions: 84 npm install --save-dev html-webpack-plugin@3.2.0 // htmlの移動 mv dist/index.html src/index.html rm dist/main.js |
インストールが完了したらbabelの設定ファイルを作成します。
1 |
touch .babelrc |
この設定ファイルにはbabelによって変換する対象を記述します。
下記だとESとreactコードを変換対象にしています。
1 2 3 4 |
// https://babeljs.io/setup#installation { "presets": ["@babel/preset-env", "@babel/preset-react"] } |
次は、webpack.config.jsに設定を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
const path = require('path') const HtmlWebPackPlugin = require('html-webpack-plugin') // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: "babel-loader" }, { test: /\.css$/, use: [ 'style-loader', // loaderは、設定から逆順に実行されるので注意 'css-loader' ] }, { test: /\.(jpe?g|png|gif|svg|ico)$/i,// i -> 大文字小文字むし loader: 'url-loader', options: { limit: 2048, name: './images/[name].[ext]' // 2KBを超える画像ファイルは、imagesフォルダ配下に分離して配信 } }, { test: /\.scss$/, use: [ 'style-loader', // loaderは、設定から逆順に実行されるので注意 'css-loader', 'sass-loader' ] }, { test: /\.html$/, loader: 'html-loader' } ] }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 }, plugins: [ new HtmlWebPackPlugin({ template: './src/index.html', // 雛形のHTMLのファイル名 filename: './index.html' // 最終的に生成されるHTMLファイル名 }) ] } |
続いて、index.jsを書き換えます。
1 2 3 4 5 6 7 8 9 |
// src/index.js import React from 'react' import ReactDOM from 'react-dom' ReactDOM.render( <div>Hello, React!</div>, document.getElementById('root') ) |
続いて、index.htmlを書き換えます。
1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Hi, webpack!!!!</title> </head> <body> <div id="root"></div> </body> </html> |
ここまで設定したらサーバーを起動しましょう。
1 |
npm run start |
mini-css-etract-plugin導入
mini-css-extract-pluginで、CSSの圧縮ができます。
1 2 3 4 5 |
$ npm info mini-css-extract-plugin mini-css-extract-plugin@0.8.0 | MIT | deps: 4 | versions: 13 npm install --save-dev mini-css-extract-plugin@0.8.0 |
webpack.config.jsを修正します。
cssとscssの設定もここで一つにしてしまいましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
const path = require('path') const HtmlWebPackPlugin = require('html-webpack-plugin') // new const MiniCssExtractPlugin = require('mini-css-extract-plugin') // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: "babel-loader" }, { test: /\.(jpe?g|png|gif|svg|ico)$/i,// i -> 大文字小文字むし loader: 'url-loader', options: { limit: 2048, name: './images/[name].[ext]' // 2KBを超える画像ファイルは、imagesフォルダ配下に分離して配信 } }, { // fix test: /\.(sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, // style-loaderは、styleタグ内にcssをロードするが、別ファイルに分けるので不要 // 'style-loader', // loaderは、設定から逆順に実行されるので注意 'css-loader', 'sass-loader' ] }, { test: /\.html$/, loader: 'html-loader' } ] }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 }, plugins: [ new HtmlWebPackPlugin({ template: './src/index.html', // 雛形のHTMLのファイル名 filename: './index.html' // 最終的に生成されるHTMLファイル名 }), // new new MiniCssExtractPlugin({ filename: '[name].[hash].css' // importしたcssをこの形式にしてバンドルする(name->デフォルトはmain) }) ] } |
index.jsの修正をします。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// src/index.js import React from 'react' import ReactDOM from 'react-dom' // new import './style.css' import './style.scss' ReactDOM.render( <div>Hello, React!</div>, document.getElementById('root') ) |
index.htmlの修正をします。
1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Hi, webpack!!!!</title> </head> <body class="haikei"><!-- クラス追加 --> <div id="root"></div> </body> </html> |
サーバーを起動しましょう。
1 |
npm run start |
uglifyjs-webpack-plugin
uglifyjs-webpack-pluginにて、console.logの自動削除をしましょう。
1 2 3 4 5 |
npm info uglifyjs-webpack-plugin uglifyjs-webpack-plugin@2.2.0 | MIT | deps: 9 | versions: 50 npm install --save-dev uglifyjs-webpack-plugin@2.2.0 |
package.jsonのscriptsにlaunchを追加します。
1 2 3 4 |
"scripts": { "launch": "webpack-dev-server --open --mode production", "start": "webpack-dev-server --open --mode development" }, |
npm run launchコマンドを実行したときは、本番環境用の環境が立ち上がります。
webpack.config.jsにも設定を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
const path = require('path') const HtmlWebPackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // new const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: "babel-loader" }, { test: /\.(jpe?g|png|gif|svg|ico)$/i,// i -> 大文字小文字むし loader: 'url-loader', options: { limit: 2048, name: './images/[name].[ext]' // 2KBを超える画像ファイルは、imagesフォルダ配下に分離して配信 } }, { test: /\.(sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, // style-loaderは、styleタグ内にcssをロードするが、別ファイルに分けるので不要 // 'style-loader', // loaderは、設定から逆順に実行されるので注意 'css-loader', 'sass-loader' ] }, { test: /\.html$/, loader: 'html-loader' } ] }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 }, plugins: [ new HtmlWebPackPlugin({ template: './src/index.html', // 雛形のHTMLのファイル名 filename: './index.html' // 最終的に生成されるHTMLファイル名 }), new MiniCssExtractPlugin({ filename: '[name].[hash].css' // importしたcssをこの形式にしてバンドルする(name->デフォルトはmain) }) ], // new optimization: { minimizer: [new UglifyJsPlugin({ uglifyOptions: { compress: { drop_console: true } } })], }, } |
UglifyJsPluginは、内部でUglifyJSを使っています。
1 |
drop_console (default: false) -- Pass true to discard calls to console.* functions. If you wish to drop a specific function call such as console.info and/or retain side effects from function arguments after dropping the function call then use pure_funcs instead. |
上記の通り、cconsole.logを消したい場合は、drop_consoleの設定をtrueにする必要があります。
index.jsの中にconsole.logを仕込んでみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// src/index.js import React from 'react' import ReactDOM from 'react-dom' import './style.css' import './style.scss' console.log('Output log!!!') ReactDOM.render( <div>Hello, React!</div>, document.getElementById('root') ) |
次のコマンドを実行して、ログが出力されるか確認してください。
1 2 3 4 5 |
# 開発環境(ログ出力有) npm run start # 本番環境(ログ出力無) npm run launch |
optimize-css-assets-webpack-plugin 導入
本番環境下で圧縮したCSSファイルを配信するためのプラグインを導入します。
1 2 3 4 5 |
npm info optimize-css-assets-webpack-plugin optimize-css-assets-webpack-plugin@5.0.3 | MIT | deps: 2 | versions: 20 npm install --save-dev optimize-css-assets-webpack-plugin@5.0.3 |
webpack.config.jsに設定を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
const path = require('path') const HtmlWebPackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); // new const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: "babel-loader" }, { test: /\.(jpe?g|png|gif|svg|ico)$/i,// i -> 大文字小文字むし loader: 'url-loader', options: { limit: 2048, name: './images/[name].[ext]' // 2KBを超える画像ファイルは、imagesフォルダ配下に分離して配信 } }, { test: /\.(sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, // style-loaderは、styleタグ内にcssをロードするが、別ファイルに分けるので不要 // 'style-loader', // loaderは、設定から逆順に実行されるので注意 'css-loader', 'sass-loader' ] }, { test: /\.html$/, loader: 'html-loader' } ] }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 }, plugins: [ new HtmlWebPackPlugin({ template: './src/index.html', // 雛形のHTMLのファイル名 filename: './index.html' // 最終的に生成されるHTMLファイル名 }), new MiniCssExtractPlugin({ filename: '[name].[hash].css' // importしたcssをこの形式にしてバンドルする(name->デフォルトはmain) }) ], // fix optimization: { minimizer: [ new UglifyJsPlugin({ uglifyOptions: { compress: { drop_console: true } } }), // new new OptimizeCSSAssetsPlugin({}), ], }, } |
次のコマンドでビルドしてみましょう。
1 |
npx webpack --mode production |
dist配下に「main.hash.css」ファイルができるので、開いてみましょう。
1 |
.haikei{background-color:#8dd6f9}div{color:tomato} |
見事に圧縮されていますね。
ソースマップの導入
バンドルされたコードのエラー追跡は難しいです。
ソースマップを作成して、エラー追跡をやりやすくしましょう。
Devtoolを見ると色々なツールがありそうですが、「eval-source-map」を使います。
webpack.config.jsの最後尾に次の設定をいれてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
const path = require('path') const HtmlWebPackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist') console.log({outputPath}) // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath // 出力場所 }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: "babel-loader" }, { test: /\.(jpe?g|png|gif|svg|ico)$/i,// i -> 大文字小文字むし loader: 'url-loader', options: { limit: 2048, name: './images/[name].[ext]' // 2KBを超える画像ファイルは、imagesフォルダ配下に分離して配信 } }, { test: /\.(sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, // style-loaderは、styleタグ内にcssをロードするが、別ファイルに分けるので不要 // 'style-loader', // loaderは、設定から逆順に実行されるので注意 'css-loader', 'sass-loader' ] }, { test: /\.html$/, loader: 'html-loader' } ] }, devServer: { contentBase: outputPath // webpackでブラウザ起動後、初期表示する場所を指定 }, plugins: [ new HtmlWebPackPlugin({ template: './src/index.html', // 雛形のHTMLのファイル名 filename: './index.html' // 最終的に生成されるHTMLファイル名 }), new MiniCssExtractPlugin({ filename: '[name].[hash].css' // importしたcssをこの形式にしてバンドルする(name->デフォルトはmain) }) ], optimization: { minimizer: [ new UglifyJsPlugin({ uglifyOptions: { compress: { drop_console: true } } }), new OptimizeCSSAssetsPlugin({}), ], }, // new devtool: 'eval-source-map' } |
index.jsに誤った関数を入力してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// src/index.js import React from 'react' import ReactDOM from 'react-dom' import './style.css' import './style.scss' // 誤った関数 consoooole.log('Output log!!!') ReactDOM.render( <div>Hello, React!</div>, document.getElementById('root') ) |
サーバーを起動して、コンソールを確認してみてください。
1 |
npm run start |
余力がある人は、devtoolの設定を外した状態でもエラーを確認してみてください。
eslint-loaderの導入
最後にeslint-loaderを導入します。
これは、構文チェックなどによく使われるものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
npm info eslint eslint@6.7.0 | MIT | deps: 37 | versions: 237 npm install --save-dev eslint@6.7.0 npm info eslint-loader eslint-loader@3.0.2 | MIT | deps: 5 | versions: 45 npm install --save-dev eslint-loader@3.0.2 npm info babel-eslint babel-eslint@10.0.3 | MIT | deps: 6 | versions: 133 npm install --save-dev babel-eslint@10.0.3 |
次のコマンドで初期化します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
npx eslint --init ? How would you like to use ESLint? To check syntax, find problems, and enforce code style ? What type of modules does your project use? CommonJS (require/exports) ? Which framework does your project use? React ? Does your project use TypeScript? No ? Where does your code run? Browser ? How would you like to define a style for your project? Use a popular style guide ? Which style guide do you want to follow? Airbnb: https://github.com/airbnb/javascript ? What format do you want your config file to be in? JSON Checking peerDependencies of eslint-config-airbnb@latest The config that you've selected requires the following dependencies: eslint-plugin-react@^7.14.3 eslint-config-airbnb@latest eslint@^5.16.0 || ^6.1.0 eslint-plugin-import@^2.18.2 eslint-plugin-jsx-a11y@^6.2.3 eslint-plugin-react-hooks@^1.7.0 ? Would you like to install them now with npm? Yes |
このコマンドの実行に成功すると「.eslintrc.json」が作成されます。
webpack.config.jsの設定を変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
const path = require('path'); const HtmlWebPackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); // パスの結合(絶対パス) const outputPath = path.resolve(__dirname, 'dist'); console.log({ outputPath }); // https://webpack.js.org/configuration/ module.exports = { entry: './src/index.js', // モジュールバンドルの対象(この中に読み込んでいるモジュールもパッケージ化される) output: { filename: 'main.js', // 出力ファイル名 path: outputPath, // 出力場所 }, module: { rules: [ // new { enforce: 'pre', // 一番早く実行するオプション test: /\.jsx?$/, exclude: /node_modules/, loader: 'eslint-loader', }, { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', }, { test: /\.(jpe?g|png|gif|svg|ico)$/i, // i -> 大文字小文字むし loader: 'url-loader', options: { limit: 2048, name: './images/[name].[ext]', // 2KBを超える画像ファイルは、imagesフォルダ配下に分離して配信 }, }, { test: /\.(sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, // style-loaderは、styleタグ内にcssをロードするが、別ファイルに分けるので不要 // 'style-loader', // loaderは、設定から逆順に実行されるので注意 'css-loader', 'sass-loader', ], }, { test: /\.html$/, loader: 'html-loader', }, ], }, devServer: { contentBase: outputPath, // webpackでブラウザ起動後、初期表示する場所を指定 }, plugins: [ new HtmlWebPackPlugin({ template: './src/index.html', // 雛形のHTMLのファイル名 filename: './index.html', // 最終的に生成されるHTMLファイル名 }), new MiniCssExtractPlugin({ filename: '[name].[hash].css', // importしたcssをこの形式にしてバンドルする(name->デフォルトはmain) }), ], optimization: { minimizer: [ new UglifyJsPlugin({ uglifyOptions: { compress: { drop_console: true, }, }, }), new OptimizeCSSAssetsPlugin({}), ], }, devtool: 'eval-source-map', }; |
次のコマンドを実行しましょう。
1 2 3 4 5 |
# シンタックスチェック npx eslint ./src/index.js # eslint(fixオプションでエラー修正する) npx eslint --fix ./src/index.js |
eslintでチェック後、次のエラーが表示されると思います。
1 |
11:3 error JSX not allowed in files with extension '.js' react/jsx-filename-extension |
JSXは、JavaScriptファイル内に書くことは推奨されていないため、構文エラーになります。
しかし、JSXは、JavaScript内にあっても問題ないと思うため、設定で回避します。
.eslintrc.jsonに次の設定を追加してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
{ "env": { "browser": true, "commonjs": true, "es6": true }, "extends": [ "plugin:react/recommended", "airbnb" ], "parser": "babel-eslint", "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parserOptions": { "ecmaFeatures": { "jsx": true }, "ecmaVersion": 2018 }, "plugins": [ "react" ], // new "rules": { "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }] } } |
もう一度、以下のコマンドを実行してください。
1 |
npx eslint ./src/index.js |
今度は、エラーが消えているはずです。
おわりに
長くなりましたが、webpackの使い方は以上です。
JavaScript、CSS, SCSS, 画像ファイルを一つのJavaScriptファイルに纏めて管理するこのツールは大変優秀ですね。
まだまだ使い方に慣れていないので、仕事・プライベート問わず、ガンガン導入していきたいと思います。
それでは、また!
関連
こちらもどうぞ。
コメントを残す
コメントを投稿するにはログインしてください。