かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その7 )( webpack + browser-sync をインストールする )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その6 )( URL の決定 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 各画面の HTML を作成する前に、今回は HTML の確認を Tomcat ではなく webpack + browser-sync を使用して行ってみようと思います。
    • まずはインストールして試してみます。
  • 「Spring Boot + npm + Geb で入力フォームを作ってテストする」では webpack は js ファイルの開発にしか使わないので HTML の確認だけなら browser-sync だけでいいのですが、両方インストールして変更したファイル(html ファイルだけでなく js ファイルも)を watch する仕組みを構築します。

参照したサイト・書籍

  1. 最新版で学ぶwebpack 3入門 – JavaScript開発で人気のバンドルツール
    https://ics.media/entry/12140

  2. Building Static Sites with Webpack
    https://blog.binoy.io/building-static-sites-with-webpack-4fc489ceabca

  3. WebPack-Dev-ServerからBrowsersyncに乗り換えた
    https://saku.io/move-from-webpack-dev-server-to-browser-sync/

  4. webpackで複数のディレクトリへ出力する
    http://webdesign-dackel.com/2015/09/10/webpack-multiple-output/

  5. window.onloadとjQueryの$(document).ready等の比較
    https://rcmdnk.com/blog/2015/07/11/computer-javascript-jquery/

目次

  1. webpack をインストールする
  2. browser-sync をインストールする
  3. webpack + browser-sync 起動用の npm-scripts を作成する
  4. 動作確認する

手順

webpack をインストールする

npm install --save-dev webpack コマンドを実行して webpack をインストールします。

f:id:ksby:20170724061949p:plain f:id:ksby:20170724062146p:plain f:id:ksby:20170724062319p:plain f:id:ksby:20170724062428p:plain f:id:ksby:20170724062534p:plain

プロジェクトのルートディレクトリ直下に webpack.config.js というファイルを新規作成し、以下の内容を記述します。

module.exports = {
    entry: {
        "js/app": ["./src/main/assets/js/app.js"]
    },
    output: {
        path: __dirname + "/src/main/resources/static",
        publicPath: "/",
        filename: "[name].js"
    }
};
  • js ファイルは src/main の下に assets/js ディレクトリを作成し、その下に作成します。
  • 今回は動作確認のために app.js を作成します。
  • webpack により処理・生成された js ファイルは src/main/resources/static/js の下に配置します。

src/main の下に assets/js ディレクトリを作成し、src/main/assets/js の下に app.js を作成します。

f:id:ksby:20170724063549p:plain

app.js には以下の内容を記述します。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

webpack を起動しやすくするために package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "webpack": "webpack"
  },

動作確認します。まずは npm run webpack -- --version コマンドを実行してバージョン番号が表示されることを確認します。

f:id:ksby:20170724064207p:plain

3.3.0 と表示されました。

次に npm run webpack コマンドを実行し、src/main/assets/js/app.js から src/main/resources/static/js/app.js が生成されることを確認します。

f:id:ksby:20170724064532p:plain

コマンドが正常に終了し、src/main/resources/static/js の下に app.js が生成されています。

f:id:ksby:20170724064722p:plain

app.js を開くと以下の内容で始まるファイルでした。

f:id:ksby:20170724064903p:plain

問題なく動作しているようですので、次へ進みます。

browser-sync をインストールする

npm install --save-dev browser-sync コマンドを実行して browser-sync をインストールします。

f:id:ksby:20170724065807p:plain f:id:ksby:20170724065903p:plain f:id:ksby:20170724070002p:plain f:id:ksby:20170724070103p:plain f:id:ksby:20170724070208p:plain f:id:ksby:20170724070311p:plain

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "browser-sync": "browser-sync"
  },

npm run browser-sync -- init コマンドを実行して bs-config.js を生成します。

f:id:ksby:20170724070615p:plain

bs-config.js の以下の点を変更します。

  • "weinre": { "port": 8080 }"weinre": { "port": 9081 } に変更します。
  • "files": false"files": [ "./static/**/*", "./src/main/resources/static/**/*" ] に変更します。
  • "server": false"server": { "baseDir": [ "./static", "./src/main/resources/static" ] }に変更します。
  • "port": 3000"port": 9080 に変更します。
  • "notify": true"notify": false に変更します。
/*
 |--------------------------------------------------------------------------
 | Browser-sync config file
 |--------------------------------------------------------------------------
 |
 | For up-to-date information about the options:
 |   http://www.browsersync.io/docs/options/
 |
 | There are more options than you see here, these are just the ones that are
 | set internally. See the website for more info.
 |
 |
 */
module.exports = {
    "ui": {
        "port": 3001,
        "weinre": {
            "port": 9081
        }
    },
    "files": [
        "./static/**/*",
        "./src/main/resources/static/**/*"
    ],
    "watchEvents": [
        "change"
    ],
    "watchOptions": {
        "ignoreInitial": true
    },
    "server": {
        "baseDir": [
            "./static",
            "./src/main/resources/static"
        ]
    },
    "proxy": false,
    "port": 9080,
    "middleware": false,
    "serveStatic": [],
    "ghostMode": {
        "clicks": true,
        "scroll": true,
        "location": true,
        "forms": {
            "submit": true,
            "inputs": true,
            "toggles": true
        }
    },
    "logLevel": "info",
    "logPrefix": "Browsersync",
    "logConnections": false,
    "logFileChanges": true,
    "logSnippet": true,
    "rewriteRules": [],
    "open": "local",
    "browser": "default",
    "cors": false,
    "xip": false,
    "hostnameSuffix": false,
    "reloadOnRestart": false,
    "notify": false,
    "scrollProportionally": true,
    "scrollThrottle": 0,
    "scrollRestoreTechnique": "window.name",
    "scrollElements": [],
    "scrollElementMapping": [],
    "reloadDelay": 0,
    "reloadDebounce": 0,
    "reloadThrottle": 0,
    "plugins": [],
    "injectChanges": true,
    "startPath": null,
    "minify": true,
    "host": null,
    "localOnly": false,
    "codeSync": true,
    "timestamps": true,
    "clientEvents": [
        "scroll",
        "scroll:element",
        "input:text",
        "input:toggles",
        "form:submit",
        "form:reset",
        "click"
    ],
    "socket": {
        "socketIoOptions": {
            "log": false
        },
        "socketIoClientConfig": {
            "reconnectionAttempts": 50
        },
        "path": "/browser-sync/socket.io",
        "clientPath": "/browser-sync",
        "namespace": "/browser-sync",
        "clients": {
            "heartbeatTimeout": 5000
        }
    },
    "tagNames": {
        "less": "link",
        "scss": "link",
        "css": "link",
        "jpg": "img",
        "jpeg": "img",
        "png": "img",
        "svg": "img",
        "gif": "img",
        "js": "script"
    }
};

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "browser-sync:start": "browser-sync start --config bs-config.js"
  },

プロジェクトのルートディレクトリ直下に static ディレクトリを作成します。今回は Thymeleaf テンプレート前の html ファイルは jar ファイルに入れないようにするために、src/main/resources/static ではなく static の下に作成することにします。

npm run browser-sync:start コマンドを実行してみます。

f:id:ksby:20170724072032p:plain

特にエラーは出ずに起動しているようです。http://localhost:3001 にアクセスすると browser-sync の ui が表示されました。

f:id:ksby:20170724072148p:plain

npm run browser-sync:start コマンドを実行したコマンドプロンプトで Ctrl+C を押して終了すると、ブラウザに以下の画面が表示されました。サーバと同期して動いているようです。

f:id:ksby:20170724072416p:plain

問題なさそうですので、次へ進みます。

webpack + browser-sync 起動用の npm-scripts を作成する

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p webpack:watch browser-sync:start"
  },
  • "webpack:watch": "webpack --watch" を追加します。
  • "server": "run-p webpack:watch browser-sync:start" を追加します。

npm run server コマンドで webpack と browser-sync が起動して、ファイルが変更されるとブラウザに自動反映されるようにします。

動作確認する

static の下に以下の内容の sample.html を作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
テスト用ページです。<br/>
<span id="sample">xxx</span>
<script src="/js/app.js"></script>
</body>
</html>

src/main/assets/js/app.js を以下の内容に変更します。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

$(document).ready(function() {
    $("#sample").text("サンプル");
});

npm run server コマンドを実行します。

f:id:ksby:20170724213650p:plain

ブラウザが起動しますので、http://localhost:9080/sample.html にアクセスしてみます。

f:id:ksby:20170724213858p:plain

sample.html に記述した文字列が表示されており、また app.js に記述した処理も反映されていました。

sample.html を以下の内容に変更してみます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
サンプルですよ?<br/>
<span id="sample">xxx</span>
<script src="/js/app.js"></script>
</body>
</html>

ブラウザを見るとリロードすることなく反映されています。

f:id:ksby:20170724214205p:plain

今度は app.js を以下の内容に修正してみます。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

$(document).ready(function() {
    $("#sample").text("app.js を修正してみました");
});

webpack の処理が実行されて src/main/resources/static/js/app.js が作り直されて、

f:id:ksby:20170724214540p:plain

ブラウザの方に手動でリロードすることなく反映されました。

f:id:ksby:20170724214748p:plain

他にも browser-sync はこんなことができるようです。

  • 複数のブラウザ(下の画像では IEChrome)で同じページを見ている時に、ファイルが変更されると全てのブラウザに手動リロードすることなく反映されます。

    f:id:ksby:20170724223414p:plain

  • 複数のブラウザで同じフォームを見ている時に、片方のブラウザ(IE)に入力すると、自動で別のブラウザ(Chrome)にも入力されます。

    f:id:ksby:20170724223724p:plain

  • sample2.html を作成します。その後で sample.html には sample.html → sample2.html へ遷移するリンクを、sample2.html には sample2.html → sample.html へ遷移するリンクを記述してから IEChrome で sample.html を表示した後

    f:id:ksby:20170724224224p:plain

    IE の方のリンクをクリックして sample2.html へ遷移すると、Chrome の方でも sample2.html へ遷移します。

    f:id:ksby:20170724224858p:plain

browser-sync ってすごい面白い! まさか、こんな便利なソフトがあるとは。

他にも browser-sync には proxy の機能もあるようで、Spring Boot の Tomcat と連携して自動リロードもできるのでしょうか。。。? 入力フォームができたら試してみたいと思います。

Ctrl+C を押して webpack と browser-sync を停止します。今回作成した sample.html, sample2.html は commit せずに削除します。

最後に package.json, webpack.config.js, bs-config.js を記載しておきます。

ソースコード

package.json

{
  "name": "boot-npm-geb-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons",
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p webpack:watch browser-sync:start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "admin-lte": "^2.3.11",
    "bootstrap": "^3.3.7",
    "font-awesome": "^4.7.0",
    "ionicons": "^3.0.0"
  },
  "devDependencies": {
    "browser-sync": "^2.18.13",
    "cpx": "^1.5.0",
    "npm-run-all": "^4.0.2",
    "rimraf": "^2.6.1",
    "webpack": "^3.3.0"
  }
}

webpack.config.js

module.exports = {
    entry: {
        "js/app": ["./src/main/assets/js/app.js"]
    },
    output: {
        path: __dirname + "/src/main/resources/static",
        publicPath: "/",
        filename: "[name].js"
    }
};

bs-config.js

/*
 |--------------------------------------------------------------------------
 | Browser-sync config file
 |--------------------------------------------------------------------------
 |
 | For up-to-date information about the options:
 |   http://www.browsersync.io/docs/options/
 |
 | There are more options than you see here, these are just the ones that are
 | set internally. See the website for more info.
 |
 |
 */
module.exports = {
    "ui": {
        "port": 3001,
        "weinre": {
            "port": 9081
        }
    },
    "files": [
        "./static/**/*",
        "./src/main/resources/static/**/*"
    ],
    "watchEvents": [
        "change"
    ],
    "watchOptions": {
        "ignoreInitial": true
    },
    "server": {
        "baseDir": [
            "./static",
            "./src/main/resources/static"
        ]
    },
    "proxy": false,
    "port": 9080,
    "middleware": false,
    "serveStatic": [],
    "ghostMode": {
        "clicks": true,
        "scroll": true,
        "location": true,
        "forms": {
            "submit": true,
            "inputs": true,
            "toggles": true
        }
    },
    "logLevel": "info",
    "logPrefix": "Browsersync",
    "logConnections": false,
    "logFileChanges": true,
    "logSnippet": true,
    "rewriteRules": [],
    "open": "local",
    "browser": "default",
    "cors": false,
    "xip": false,
    "hostnameSuffix": false,
    "reloadOnRestart": false,
    "notify": false,
    "scrollProportionally": true,
    "scrollThrottle": 0,
    "scrollRestoreTechnique": "window.name",
    "scrollElements": [],
    "scrollElementMapping": [],
    "reloadDelay": 0,
    "reloadDebounce": 0,
    "reloadThrottle": 0,
    "plugins": [],
    "injectChanges": true,
    "startPath": null,
    "minify": true,
    "host": null,
    "localOnly": false,
    "codeSync": true,
    "timestamps": true,
    "clientEvents": [
        "scroll",
        "scroll:element",
        "input:text",
        "input:toggles",
        "form:submit",
        "form:reset",
        "click"
    ],
    "socket": {
        "socketIoOptions": {
            "log": false
        },
        "socketIoClientConfig": {
            "reconnectionAttempts": 50
        },
        "path": "/browser-sync/socket.io",
        "clientPath": "/browser-sync",
        "namespace": "/browser-sync",
        "clients": {
            "heartbeatTimeout": 5000
        }
    },
    "tagNames": {
        "less": "link",
        "scss": "link",
        "css": "link",
        "jpg": "img",
        "jpeg": "img",
        "png": "img",
        "svg": "img",
        "gif": "img",
        "js": "script"
    }
};

履歴

2017/07/24
初版発行。