かんがるーさんの日記

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

Gradle で Multi-project を作成する ( その15 )( vuejs+springboot編、frontend-app プロジェクトを作成する )

概要

記事一覧はこちらです。

Gradle で Multi-project を作成する ( その14 )( vuejs+springboot編、Multi-project のベースと backend-app プロジェクトを作成する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • frontend を Vue.js で、backend を Spring Boot でアプリケーションを実装し、各アプリケーションをサブプロジェクトとする Gradle Multi-project のサンプルを作成します。
    • 今回は Vue.js のアプリケーション(frontend)を作成し、gradle build で実行可能 jar にまとめます。

参照したサイト・書籍

  1. Vue CLI
    https://cli.vuejs.org/

  2. Vuetify
    https://vuetifyjs.com/en/

  3. How to build a web app with Vue, Vuetify and Axios
    https://medium.com/javascript-in-plain-english/implement-movie-app-with-vue-vuetify-axios-open-movie-database-api-d12290318cf9

  4. axios、async/awaitを使ったHTTPリクエスト(Web APIを実行)
    https://qiita.com/shisama/items/61cdcc09dc69fd8d3127

  5. async関数においてtry/catchではなくawait/catchパターンを活用する
    https://qiita.com/akameco/items/cc73afcdb5ac5d0774bc

  6. Vue.jsでビューの変更がされないときに疑うこと+主な解決策方法
    https://cloudpack.media/41984

  7. Vue Test Utils
    https://vue-test-utils.vuejs.org/

  8. Vue.jsのテストでコンポーネントをいい感じにwrapする方法
    https://qiita.com/ykhirao/items/8e8a9547a693c677813c

  9. Vue CLI - Configuration Reference - devServer.proxy
    https://cli.vuejs.org/config/#devserver-proxy

  10. Gradle Docs - The Base Plugin
    https://docs.gradle.org/current/userguide/base_plugin.html

  11. node-gradle/gradle-node-plugin
    https://github.com/node-gradle/gradle-node-plugin

  12. node-gradle/gradle-node-plugin - Node Plugin
    https://github.com/node-gradle/gradle-node-plugin/blob/master/docs/node.md

目次

  1. Vue.js のアプリケーション(frontend)(frontend-app)を作成する
    1. Vue CLI をインストールする
    2. Vue CLI で frontend-app プロジェクトを作成する
    3. Vuetify をインストールする
    4. npm run buildnpm run lintnpm run test:unit 実行時に prettier で自動フォーマットするよう設定する
    5. WebAPI を呼び出して取得したデータを画面に表示する処理を実装する
    6. vue.config.js を作成して webpack-dev-server に proxy の設定を追加する
    7. 動作確認
  2. Gradle の build タスク実行時に npm run test:unitnpm run build が実行されるようにする
  3. Gradle の build タスク実行時に frontend-app の dist ディレクトリの下にあるファイルを backend-app の src/main/resources/static にコピーする
  4. settings.gradle に sample-cmdapp プロジェクトの include 文を追加する
  5. clean タスク実行 → Rebuild Project 実行 → build タスク実行を行う
  6. 実行可能 jar から Tomcat を起動して動作確認する
  7. 次回は。。。

手順

Vue.js のアプリケーション(frontend)(frontend-app)を作成する

Vue CLI をインストールする

以下のコマンドを実行して Vue CLI をインストールします。

  • npm install -g @vue/cli
  • npm install -g @vue/cli-service-global

f:id:ksby:20190506093206p:plain

以下の警告メッセージが出ていますが、TypeScript、GraphQL は使用しないので今回は何もしません。

  • ts-node@8.1.0 requires a peer of typescript@>=2.0 but none is installed.
  • apollo-tracing@0.5.2 requires a peer of graphql@0.10.x - 14.1.x but none is installed.

インストールされた Vue CLI のバージョンを vue --version コマンドで確認すると 3.7.0 でした。

f:id:ksby:20190506101848p:plain

Vue CLI で frontend-app プロジェクトを作成する

コマンドラインから vue create frontend-app を実行して frontend-app プロジェクトを作成します。

f:id:ksby:20190506093745p:plain ※「Please pick a preset:」では「Manually select features」を選択します。

f:id:ksby:20190506093932p:plain ※「Check the features needed for your project:」はデフォルトで「Babel」「Linter / Formatter」が選択済みで、「Unit Testing」を追加で選択します。

f:id:ksby:20190506094156p:plain ※「Pick a linter / formatter config:」では「ESLint + Prettier」を選択します。

f:id:ksby:20190506094643p:plain ※「Pick additional lint features:」はデフォルトで「Lint on save」が選択済みで、そのままにします。

f:id:ksby:20190506094804p:plain ※「Pick a unit testing solution:」では「Jest」を選択します。

f:id:ksby:20190506094922p:plain ※「Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys)」では「In dedicated config files」を選択します。

f:id:ksby:20190506095230p:plain ※「Save this as a preset for future projects? (y/N)」は何も入力せずに Enter キーを押します。

インストールが実行されます。

f:id:ksby:20190506095625p:plain

インストール後 frontend-app ディレクトリに移動してから npm run serve コマンドを実行して、

f:id:ksby:20190506101444p:plain f:id:ksby:20190506101541p:plain

http://localhost:8080/ にアクセスすると画面が表示されました。

f:id:ksby:20190506101648p:plain

npm run build コマンドを実行すると、警告・エラーは出ずに終了します。

f:id:ksby:20190506103005p:plain

IntelliJ IDEA の Project Tool Window で frontend-app のディレクトリ構成を見てみると以下のようになっていました。尚、IntelliJ IDEA で最初に表示させる時には「Indexing…」のメッセージが表示されてしばらく時間がかかります(おそらく node_modules の下の大量のファイルを index するのに時間がかかっているためでしょう)。

f:id:ksby:20190506103059p:plain

Vuetify をインストールする

Quick start の記述に従い vue add vuetify コマンドを実行します。

f:id:ksby:20190506110432p:plain ※「Choose a preset: (Use arrow keys)」はデフォルトの「Default (recommended)」のままにします。

f:id:ksby:20190506110617p:plain

インストール直後の状態で npm run build コマンドを実行すると prettier のフォーマットと異なるために eslint が警告を大量に出すので、npm run build コマンド実行時に prettier で自動フォーマットして警告が出ないようにします。

npm run buildnpm run lintnpm run test:unit 実行時に prettier で自動フォーマットするよう設定する

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

f:id:ksby:20190506133306p:plain

frontend-app/package.json"scripts": { ... } を以下のように変更します。

  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "run-s prettier:format vue-cli-service:build",
    "lint": "run-s prettier:format vue-cli-service:lint",
    "test:unit": "run-s prettier:format vue-cli-service:test:unit",
    "vue-cli-service:build": "vue-cli-service build",
    "vue-cli-service:lint": "vue-cli-service lint",
    "vue-cli-service:test:unit": "vue-cli-service test:unit",
    "prettier:format": "prettier --write {src,tests}/**/*.{js,vue}"
  },
  • 以下の4行を追加します。今回初めて知りましたが、Paste した時に Paste した行や1つ上の行の末尾に "," がないと IntelliJ IDEA が自動で付けてくれますね。
    • "vue-cli-service:build": "vue-cli-service build"
    • "vue-cli-service:lint": "vue-cli-service lint"
    • "vue-cli-service:test:unit": "vue-cli-service test:unit"
    • "prettier:format": "prettier --write {src,tests}/**/*.{js,vue}"
  • "build": "vue-cli-service build""build": "run-s prettier:format vue-cli-service:build" に変更します。
  • "lint": "vue-cli-service lint""lint": "run-s prettier:format vue-cli-service:lint" に変更します。
  • "test:unit": "vue-cli-service test:unit""test:unit": "run-s prettier:format vue-cli-service:test:unit" に変更します。

以上で設定は完了です。再度 npm run build コマンドを実行してみると2件 warning が出ていますが Build complete. が表示されました。

f:id:ksby:20190506135038p:plain f:id:ksby:20190506135128p:plain

2件の warning はファイルサイズや lazy load に関するものでしたので、今回は解消せずに先に進みます。

また IntelliJ IDEA 上でも prettier でフォーマットできるように設定します。メインメニューから「File」-「Settings...」を選択して「Settings」ダイアログを表示させた後、画面右上に prettier と入力して Prettier Plugin の設定画面を開いてから、画面右側の「Prettier package」に frontend-app/node_modules/prettier の絶対パスを設定します。

f:id:ksby:20190506143006p:plain

WebAPI を呼び出して取得したデータを画面に表示する処理を実装する

WebAPI を呼び出すのに axios を使用するので npm install --save axios コマンドを実行してインストールします。

f:id:ksby:20190506142722p:plain

frontend-app/src/components/HelloWorld.vue のファイル名を CallSampleWebapi.Vue に変更します。

frontend-app/src/App.vue を以下のように変更します。

<template>
  <v-app>
    <v-content>
      <CallSampleWebapi />
    </v-content>
  </v-app>
</template>

<script>
import CallSampleWebapi from "./components/CallSampleWebapi";

export default {
  name: "App",
  components: {
    CallSampleWebapi: CallSampleWebapi
  },
  data() {
    return {
      //
    };
  }
};
</script>
  • <v-toolbar app>...</v-toolbar> を削除します。
  • HelloWorldCallSampleWebapi に一括置換します。

frontend-app/src/components/CallSampleWebapi.Vue を以下のように変更します。

<template>
  <v-container>
    <v-layout text-xs-center wrap>
      <v-flex xs12>
        <div>code: {{ code }}</div>
        <div>value: {{ value }}</div>
        <v-btn color="info" v-on:click="callSampleWebapi">クリック!</v-btn>
      </v-flex>
    </v-layout>
  </v-container>
</template>

<script>
import axios from "axios";

export default {
  data: function() {
    return {
      code: "(空)",
      value: "(空)"
    };
  },
  methods: {
    callSampleWebapi: async function() {
      try {
        const response = await axios.post("/webapi/sample");
        this.$set(this, "code", response.data.code);
        this.$set(this, "value", response.data.value);
      } catch (err) {
        alert(err);
      }
    }
  }
};
</script>

<style></style>

frontend-app/tests/unit/example.spec.js もファイル名を CallSampleWebapi.spec.js に変更した後、以下の内容に変更します。

import { mount } from "@vue/test-utils";
import Vue from "vue";
import Vuetify from "vuetify";
import CallSampleWebapi from "@/components/CallSampleWebapi.vue";

describe("CallSampleWebapi.vue test", () => {
  it("init render test", () => {
    Vue.use(Vuetify);
    const wrapper = mount(CallSampleWebapi, {});
    expect(wrapper.html()).toContain("<div>code: (空)</div>");
  });
});

vue.config.js を作成して webpack-dev-server に proxy の設定を追加する

npm run serve コマンドで起動した webpack-dev-server に http://localhost:8080/webapi/sample でアクセスしたら backend-app の http://localhost:8081/webapi/sample へリクエストを転送させるための設定を行います。

frontend-app の下に vue.config.js を新規作成した後、以下の内容を記述します。changeOrigin: true を記述すると backend-app 側で @RestController アノテーションを付与したクラスに @CrossOrigin アノテーションを付与して設定しなくてもアクセスできるようになります。

module.exports = {
  devServer: {
    proxy: {
      "^/webapi/sample": {
        target: "http://localhost:8081",
        changeOrigin: true
      }
    }
  }
};

動作確認

backend-app の Tomcat を develop profile で起動してから、

f:id:ksby:20190506202707p:plain

npm run serve コマンドを実行して frontend-app の webpack-dev-server を起動した後、

f:id:ksby:20190506203121p:plain

http://localhost:8080/ にアクセスすると以下の画面が表示されます。

f:id:ksby:20190506203230p:plain

「クリック!」ボタンをクリックすると backend-app の WebAPI を呼び出して取得した code, value の値が画面に表示されました。

f:id:ksby:20190506203339p:plain

Tomcat、webpack-dev-server を停止します。

npm run test:unit コマンドを実行するとテストも成功します。

f:id:ksby:20190506203722p:plain

npm run build コマンドも2件の warning は出たままですが、他の警告・エラーは出ずに Build complete. が表示されました。

f:id:ksby:20190506204058p:plain f:id:ksby:20190506204152p:plain

Gradle の build タスク実行時に npm run test:unitnpm run build が実行されるようにする

frontend-app ディレクトリの下に build.gradle を新規作成し、以下の内容を記述します。

plugins {
    id "base"
    id "com.github.node-gradle.node" version "1.3.0"
}

clean.delete "dist"
task npmTestUnit(type: NpmTask) {
    args = ["run", "test:unit", "2>&1"]
    execOverrides {
        it.standardOutput = new ByteArrayOutputStream()
    }
}
task npmBuild(type: NpmTask) {
    args = ["run", "build", "2>&1"]
    execOverrides {
        it.standardOutput = new ByteArrayOutputStream()
    }
}
npmBuild.dependsOn npmTestUnit
build.dependsOn npmBuild
  • id "base" を記述することで clean や build タスクが使えるようになります。
  • npm run ... コマンドの実行には gradle-node-plugin を使用します。
  • args に "2>&1" を指定して標準エラー出力を標準出力にリダイレクトし、execOverrides { it.standardOutput = new ByteArrayOutputStream() } を記述することで標準出力、標準エラー出力に何も出力されないようにします。

ちなみに標準出力、標準エラー出力の内容をファイルに出力したい場合には、以下のように記述すれば frontend-app/build/tests/stdout.txt に出力されます。

task npmTestUnit(type: NpmTask) {
    args = ["run", "test:unit", "2>&1"]

    def testsDir = "${projectDir}/build/tests"
    execOverrides {
//        it.standardOutput = new ByteArrayOutputStream()
        it.standardOutput = new FileOutputStream("${testsDir}/stdout.txt")
    }
    doFirst {
        mkdir testsDir
    }
}

Gradle の build タスク実行時に frontend-app の dist ディレクトリの下にあるファイルを backend-app の src/main/resources/static にコピーする

backend-app/build.gradle を以下のように変更します。

..........

dependencies {
    ..........
}

clean.delete fileTree("src/main/resources/static").include("**/*")
task copyDistToStatic(type: Copy) {
    from project(":frontend-app").file("dist")
    into "src/main/resources/static"
}
copyDistToStatic.dependsOn ":frontend-app:build"
processResources.dependsOn copyDistToStatic
  • clean.delete fileTree("src/main/resources/static").include("**/*")processResources.dependsOn copyDistToStatic の記述を追加します。

settings.gradle に frontend-app プロジェクトの include 文を追加する

gradle-multiprj-vuejs-springboot の settings.gradle に include 'frontend-app' を追加します。

rootProject.name = 'gradle-multiprj-vuejs-springboot'
include 'backend-app'
include 'frontend-app'

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。Gradle Tool Window に frontend-app が表示されます。

f:id:ksby:20190507230434p:plain

clean タスク実行 → Rebuild Project 実行 → build タスク実行を行う

clean タスクを実行すると backend-app は src/main/resources/static の下がクリアされて、frontend-app は dist ディレクトリが削除されます。

f:id:ksby:20190507231149p:plain

build タスクを実行すると警告・エラーは出ずに BUILD SUCCESSFUL が表示されて、

f:id:ksby:20190507231420p:plain

frontend-app の dist ディレクトリの下のディレクトリ・ファイル一式が backend-app の src/main/resources/static の下にコピーされます。

f:id:ksby:20190507231706p:plain

実行可能 jar から Tomcat を起動して動作確認する

backend-app/build/libs の下に backend-app-1.0.0-RELEASE.jar が生成されていますので、

f:id:ksby:20190507235208p:plain

コマンドラインから java -Dspring.profiles.active=product -jar backend-app-1.0.0-RELEASE.jar を実行します。

f:id:ksby:20190507235401p:plain

http://localhost:8080/ にアクセスすると以下の画面が表示されて、

f:id:ksby:20190507235509p:plain

「クリック!」ボタンをクリックすると WebAPI を呼び出して取得したデータが画面に表示されました。

f:id:ksby:20190507235605p:plain

次回は。。。

frontend-app の下に作成した build.gradle のタスクを build タスク実行時に動かすための The Base Plugin を見つけるまでが意外に時間がかかりました。書いてある記事が少ないのか、なかなか見つけられなかったんですよね。。。

これでとりあえずやりたいことは全てやったので、最後に感想を書いて終わりにします。

履歴

2019/05/08
初版発行。