webpackをちゃんと一から触ってみた(babel / React / Workbox3点セット)

いい加減何がどうなってるのかちゃんと理解して使いたかったので、最近よく使う・これから使いたいなと思っている3点(babel / React + Workbox)込みのサンプルをwebpackでビルドするようにしてみました。

プロジェクトのセットアップ

$ mkdir practice
$ cd practice
$ git init
$ npm init -y

webpackを入れる

webpackのGetting Startedを見ながらちゃんとやる。

$ npm i -D webpack webpack-cli

最低限のファイルを用意する

webpackでごにょごにょするJSファイルと、それを実際に使うindex.htmlを用意します。

$ mkdir src
$ touch index.html src/index.js
$ tree -L 1
.
├── index.html
├── node_modules
├── package-lock.json
├── package.json
└── src

src/index.js

const component = () => {
  const element = document.createElement('div')
  element.innerHTML = 'Hello webpack'
  return element
}
document.body.appendChild(component())

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Hello webpack</title>
  </head>
  <body>
    <script src="./src/index.js"></script>
  </body>
</html>

出力を見る

src/index.jsに書いた文字列が表示されるようになりました。ここまではまだwebpackでなにかしているわけではありません。

webpackでバンドルを作る

このままだと、create-react-appとかでSPAを作る時の構成といろいろ違うので、ビルドディレクトリを作って構成をあわせていきます。

distディレクトリを作る

まずはdistディレクトリにindex.htmlを配置するようにしましょう。

$ mkdir dist
$ mv index.html dist/

webpack.config.jsを作る

ここからは、srcに配置されているJSをビルドしてdistに移す必要があります。まずはそのための設定をwebpackでやっていきましょう。

$ touch webpack.config.js
$ vim webpack.config.js

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

ここでは、「src/index.jsを読み込んでdist/bundle.jsとしてアウトプットする」という内容がおおよそ書かれています。

npxでwebpackを実行する

それでは実際に先ほど書いたビルドを実行してみましょう。

$ npx webpack --config webpack.config.js 
Hash: b9fb2c380dbf57785037
Version: webpack 4.7.0
Time: 115ms
Built at: 2018-05-06 10:24:08
    Asset       Size  Chunks             Chunk Names
bundle.js  656 bytes       0  [emitted]  main
Entrypoint main = bundle.js
[0] ./src/index.js 170 bytes {0} [built]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/

treeなどで確認すると、distbundle.jsが生成されています。

$ tree dist/
dist/
├── bundle.js
└── index.html

dist/index.htmlでbundle.jsを読む

最後にindex.htmlが読み込むJSファイルをbundle.jsに変更しましょう。

<!DOCTYPE html>
<html>
  <head>
    <title>Hello webpack</title>
  </head>
  <body>
    <script src="./bundle.js"></script>
  </body>
</html>

ここまでやると、再び”Hello webpack”が表示されるようになります。

npm scriptでwebpackを実行する

このままnpxで実行する方法でもよいのですが、CI / CDで動かすことなどを考えると、npm run buildのような形で動かしたいなと思います。ということで、package.jsonscripts部分を以下のように変えましょう。

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },

これでnpm run buildするとwebpackが実行されます。

webpackでbabel

arrow functionとかJSXを使いたいとなると、babelは入れておきたくなります。ということで入れましょう。

$ npm install --save-dev babel-preset-env babel-loader babel-core
$ echo '{"presets": ["env"]}' > .babelrc 

続いてwebpack.config.jsにbabelを利用するということを書き足しましょう。

const path = require('path')
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
}

module.rulesに「.jsで終わる/node_modules/配下以外のファイルの場合、babel-loaderを読み込みます(意訳)」というコードを足しました。

この状態でnpm run buildが成功すれば、babelの利用準備も整いました。

webpack & babelでReactコンポーネントをビルドする

ここまできたら、Reactコンポーネントのビルドにトライしてみましょう。

# Reactのインストール
$ npm i -S react react-dom

# babelのReact向けプリセットのインストール
$ npm i -D babel-preset-react

# Reactのファイル設置
$ touch src/index.jsx

.babelrcの更新

.babelrcpresetsreactを追加します。これでbabelのトランスパイルでReactのプリセットが利用できるようになります。

{"presets": ["env", "react"]}

webpack.config.jsの更新

続いてwebpack側も更新します。entrymoduleの変換対象を.jsから.jsxに変更するだけですが、忘れるとこの後のReactコードのビルドがされなくなるので要注意です。

const path = require('path')
module.exports = {
  entry: './src/index.jsx',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.jsx$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
}

src/index.jsxを作成

そしてReactコンポーネントを用意しましょう。テキストを表示するコンポーネント1つと、実際のDOMにrenderする処理の2つを追加します。

import React from 'react'
import ReactDOM from 'react-dom'

const Hello = () => <h1>Hello React</h1>

ReactDOM.render(
  <Hello/>,
  document.getElementById('root')
);

dist/index.htmlの更新

最後にindex.htmlも更新します。これは上記のReactのrender処理で、id="root"の属性を持つタグが必要になったためです。

<!DOCTYPE html>
<html>
  <head>
    <title>Hello webpack</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./bundle.js"></script>
  </body>
</html>

ビルドする

ここまでできたら、npm run buildでビルドしてみましょう。正しくビルドできれば、以下のように表示されるはずです。

workboxを入れる

どうせなので+αが欲しいなと思い、workboxをいれてみました。

$ npm i -D workbox-webpack-plugin

webpack.config.jsの更新

webpack.config.jsでserviceWorkerの作成を定義します。

const path = require('path')
const { GenerateSW } = require('workbox-webpack-plugin');

module.exports = {
  entry: './src/index.jsx',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.jsx$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new GenerateSW()
  ]
}

この状態でビルドを実行すると、precache-manifest.xxxxx.jsservice-worker.jsがdist配下に生成されます。

$ tree dist/
dist/
├── bundle.js
├── index.html
├── precache-manifest.e19faf6f21b6d812a649eef7a59c2785.js
└── service-worker.js

0 directories, 4 files

index.htmlでserviceWorkerの登録

生成されたファイルを読み込ませましょう。以下の例のように、serviceWorkerが使えるブラウザのみ、service-worker.jsをレジストさせます。

<!DOCTYPE html>
<html>
  <head>
    <title>Hello webpack</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./bundle.js"></script>
    <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('./service-worker.js')
      })
    }
    </script>
  </body>
</html>

これでserviceWokerの登録と、ビルドしたファイル(bundle.js)をprecacheする処理が追加されました。

現状index.htmlがキャッシュされていませんが、bundle.jsについては一度アクセすることでキャッシュされるようになります。そのためオフライン状態でもbundle.jsへの直アクセスはエラーにならないようになります。

[PR]WordPressの入門書共著しました

ローカル環境の構築からテーマ作成・サイトの運営やコミュニティの紹介など、
「WordPressを触って終わり」ではなく、仕事でできるようになるには何が必要かがわかるようになる内容になっています。


(目次)
Lesson01:WordPressをはじめよう
Lesson02:必要な環境を整えインストールする
Lesson03:初期設定をしよう
Lesson04:コンテンツの追加・編集とナビゲーションの設定
Lesson05:テーマとプラグインによる外観カスタマイズ
Lesson06:プラグインによる機能の追加
Lesson07:ローカル開発環境をつくろう
Lesson08:テーマ作成の第一歩〜PHPとテーマの基礎
Lesson09:テンプレートファイルの作成
Lesson10:各種テンプレートファイルの作成
Lesson11:テーマカスタマイザーの実装
Lesson12:WordPressを本番環境へデプロイする
Lesson13:サイトの広報と集客
Lesson14:サイトの運営と管理
Lesson15:もっとWordPressを使いこなす・学ぶ

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です