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

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

広告ここから
広告ここまで

目次

    いい加減何がどうなってるのかちゃんと理解して使いたかったので、最近よく使う・これから使いたいなと思っている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への直アクセスはエラーにならないようになります。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark