LoginSignup
4
9

SpringBootで包み込むVue.js開発環境 カンタン構築

Last updated at Posted at 2024-02-11

はじめに

こんにちは。バックエンドエンジニアを目指して奮闘中のひろえです。

突然ですが、未経験・独学の身では、何から勉強を始めて、何に重点を置くかって大事ですよね。
私自身は JavaSpring Boot から学び、バックエンドに力を入れてきました。
しかし、ポートフォリオを作るうえでもフロントエンドやインフラに関しても最低限メジャーな技術は押さえておきたいところ。

ということで、できるだけ簡単にフロントエンドを始めるべく、希望をざっと挙げてみました。

  • フロントエンドはほとんど触ったことがないから最小構成から始めたい
  • Vue.js の単一ファイルコンポーネント (SFC) を使ってみたい
  • 静的アセットは jar ファイルに同梱し、Spring Boot の静的リソース配信機能を利用して配信したい
  • CI/CD 環境や本番環境でもシンプルに環境構築できるようにしたい

希望を叶えるため、あれこれ調べているうちに以下の記事に出会いました。

領域展開「Gradle」で Node.js を制する
Spring Boot ウェブアプリにフロントエンド技術を含める webpack ビルド

いずれの記事もとても分かりやすく丁寧に説明してくださっているので、こちらを読んでいただければ十分なのですが、フロントエンドに関する知識が少なすぎて詰まる部分があったので、この記事ではよりシンプルな構成で環境構築する方法をご紹介しようと思います。

記事中のソースコードは以下のリポジトリから参照できます。

誤りなどがあればご指摘いただけますと幸いです。

環境

  • Java 17.0.5
  • Spring Boot 3.2.2
  • gradle-node-plugin 7.0.1
  • Vue.js 3.4.18
  • Node.js 21.6.1
  • IntelliJ IDEA 2023.3.3 (Community Edition)
  • Windows 11 Pro

プロジェクト構成概要

バックエンドには Gradle プロジェクト、フロントエンドには Node.js プロジェクトを使用し、Node.js プロジェクトは Gradle プロジェクトのサブプロジェクトとして扱います。
そして gradle-node-plugin というプラグインで両ビルドサイクルを統合する、というのが今回のポイントです。

このプラグインは GradleNode.js プロジェクトを管理するためのもので、Gradle のビルドの一環として任意の Node.js スクリプトを実行させることができます。
今回の場合は、リソースを処理する Gradle タスクを開始する前に Node.js プロジェクトのビルドを実行させるように構成することで、バックエンドとフロントエンドのビルドサイクルの統合を実現しています。

なお、gradle-node-plugin は、ローカルの .gradle ディレクトリに Node.jsnpm 自動的にインストールする機能も提供しています。
Spring Initializr で作成した Spring Boot プロジェクトは Gradle Wrapper を同梱するため、これによって JDK を用意するだけで済むようになります。

※ Java の Web アプリケーションにおいて静的リソースをどのように構成するかについては、以下の記事が勉強になりました。

Java ウェブアプリプロジェクトに JavaScript/TypeScript などの静的アセットをどう配置するか

今回紹介する構成は、この記事における 「1.1. サーバサイドを Maven/Gradle で、フロントエンドを Maven/Gradle で包んだ npm/Yarn で開発するケース」に該当します。

環境構築

以下のような手順で環境を構築していきます。

1. Spring Boot プロジェクトを作成する
2. Node.js プロジェクトを作成する
3. Vue.js 開発環境を構築する

1. Spring Boot プロジェクトを作成する

はじめに Spring Initializer で Spring Boot プロジェクトを生成します。

2024-02-09-194957.png

ビルドツールは Gradle - Groovy を、言語は Java を選択します。
依存関係は最低限 spring-boot-starter-webspring-boot-starter-thymeleaf が追加されていれば問題ありません。
メタデータについては必要に応じて変更してください。

GENERATE ボタンをクリックしてプロジェクトをダウンロードします。
ダウンロードが完了したら、zip ファイルを展開し、お好みのエディタで開いてください。

以下のようなディレクトリ構成となっていると思います。

2024-02-09-195244.png

settings.gradlebuild.gradle は以下のような状態です。

settings.gradle
rootProject.name = 'demo'
build.gradle
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.2'
	id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

2. Node.js プロジェクトを作成する

続いては Node.js プロジェクトを作成します。

2.1 フロントエンドサブプロジェクトを作成する

Node.js プロジェクトは Gradle サブプロジェクトとして扱うため、まずはそのための設定を行いましょう。

まず、プロジェクトルートに新規ディレクトリを作成します。
ここではディレクトリ名を frontend としますが、お好きな名前にしていただいて構いません (その場合は以降適宜読み替えてください)。

ディレクトリの作成が完了したら、Gradle にサブプロジェクトとして認識させるために settings.gradle ファイルを以下のように変更します。

setting.gradle
rootProject.name = 'demo'
+ include 'frontend'

※ Gradle のマルチプロジェクトに関しては以下をご参照ください。

Multi-Project Build Basics (gradle.org)

2.2 gradle-node-plugin を追加する

つぎに、frontend ディレクトリ直下に frontend サブプロジェクト専用の build.gradle ファイルを作成してください。

2024-02-10-101700.png

frontend サブプロジェクトにおいて gradle-node-plugin を利用できるようにするために、この build.gradle ファイルを以下のように編集します。

build.gradle (frontend サブプロジェクト)
plugins {
    id("com.github.node-gradle.node") version "7.0.1"
}

// プロジェクトローカルに Node.js をダウンロードする
node {
    download.set(true)
    version.set("21.6.1")
}

node ブロックでは、frontend ディレクトリ配下に指定したバージョンの Node.js (とそれにバンドルされた npm ) をダウンロードする設定をしています。

他にも設定可能な項目がいくつかありますので、詳細については以下をご参照ください。

gradle-node-plugin/docs/usage.md at 7.0.2 (github.com)

追記

frontend サブプロジェクトの build.gradle には、以下も記述するように書いていましたが、実際には npmInstall タスクが npm_run_build よりも先に実行されるため、この設定は不要でした。

build.gradle (frontend サブプロジェクト)
// npm run build の前に npm install を実行する
tasks.getByName("npm_run_build") {
    dependsOn("npm_install")
}

実行順序の確認には、次のコマンドを利用しました。

$ ./gradlew npm_run_build --dry-run

:frontend:nodeSetup SKIPPED
:frontend:npmSetup SKIPPED
:frontend:npmInstall SKIPPED
:frontend:npm_run_build SKIPPED

追記ここまで

2.3 バックエンドとフロントエンドのビルドプロセスを統合する

続いて、ルートプロジェクトの build.gradle ファイルに以下を追加してください。

build.gradle (ルートプロジェクト)
tasks.named('processResources') {
    dependsOn(":frontend:npm_run_build")
}

これにより、ルートプロジェクトにおいてリソース処理を開始する前に frontend サブプロジェクトのビルドが実行されるようになります。

2.4 Node.js プロジェクトとして初期化する

最後に frontend サブプロジェクトを Node.js プロジェクトとして初期化します。

frontned ディレクトリに移動して以下のコマンドを実行してください。

$ npm init -y

コマンドの実行が完了すると、以下のような内容の package.json が自動的に作成されているかと思います。

package.json
{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
}

3. Vue.js 開発環境を構築する

それでは、Vue.js 開発環境を構築していきましょう。

Vue.js で単一ファイルコンポーネントを使用するためには、Webpack などのビルドツールを導入する必要があるため、Webpack -> Vue.js の順で設定を行います。

ここでも引き続き frontend ディレクトリで作業してください。

3.1 Webpack をインストールする

まず、以下のコマンドを実行し、Webpackwebpack-cli をインストールします。

$ npm install --save-dev webpack webpack-cli

インストールが完了すると node_modules/ ディレクトリと package-lock.json ファイルが新たに作成されているかと思います。

2024-02-10-110801.png

また、package.json ファイルには devDependencies ブロックが追記されていると思うので確認してください。

package.json
"devDependencies": {
  "webpack": "^5.90.1",
  "webpack-cli": "^5.1.4"
}

このファイルには npm run build コマンドで Webpack のビルドを実行させるために、scripts ブロックに build スクリプトを追加しておきます (test スクリプトは削除しました)。

package.json
"scripts": {
  "build": "webpack"
}

つぎに frontend ディレクトリ直下に webpack.config.js ファイルを作成し、以下のように編集してください。

2024-02-10-110818.png

webpack.config.js
const Webpack = require('webpack');
const path = require('path');

module.exports = {
  mode: 'development',
  output: {
	// ビルド成果物はルートプロジェクトの src/main/resources/static/dist に出力する
    path: path.resolve(__dirname, '../src/main/resources/static/dist'),
    // Webpack によって生成される各バンドルのファイル名 ( [name] はエントリポイント名で置き換え)
	filename: 'js/[name].bundle.js',
    // ビルド成果物を出力する前に、出力ディレクトリをクリーンアップする
	clean: true,
  },
  resolve: {
    alias: {
      // .js ファイルや .vue ファイル内で @src エイリアスを使用して src ディレクトリを参照する
      '@src': path.resolve(__dirname, 'src/'),
    },
  },
};

Node.js プロジェクト (frontend サブプロジェクト) のビルド成果物を Gradle がリソースとして認識するディレクトリ src/main/resources/static/dist/ に出力するように設定しているのがポイントです。

なお、node_modules/Node.js プロジェクトのビルド成果物に関しては、必要に応じて .gitignore に以下を追記して Git 管理から除外してください。

.gitignore (ルートプロジェクト)
frontend/node_modules
src/main/resources/static/dist

3.2 Vue.js をインストールする

Webpack の設定が完了したので、続いて Vue.js の設定を行っていきましょう。

以下のコマンドを frontend ディレクトリで実行してください。

$ npm install vue
$ npm install --save-dev vue-loader vue-template-compiler vue-style-loader css-loader

インストールが完了すると package.json の依存関係が以下のように変更されています。

package.json
"devDependencies": {
  "css-loader": "^6.10.0",
  "vue-loader": "^17.4.2",
  "vue-style-loader": "^4.1.3",
  "vue-template-compiler": "^2.7.16",
  "webpack": "^5.90.1",
  "webpack-cli": "^5.1.4"
},
"dependencies": {
  "vue": "^3.4.18"
}

webpack.config.js を以下のように編集して、Vue.js のための基本的な設定を行います。

webpack.config.js
const Webpack = require('webpack');
const path = require('path');
+ const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
  mode: 'development',
+  entry: {
+	// 共通のライブラリなどは vendor.bundle.js としてひとまとめにする
+    vendor: ['vue'],
+  },
  output: {
    // ビルド成果物はルートプロジェクトの src/main/resources/static/dist に出力する
    path: path.resolve(__dirname, '../src/main/resources/static/dist'),
    // Webpack によって生成される各バンドルのファイル名 ([name]はエントリーポイントの名前で置き換え)
    filename: 'js/[name].bundle.js',
    // ビルド成果物を出力する前に、出力ディレクトリをクリーンアップする
    clean: true,
  },
+  plugins: [
+    // .vue ファイルをバンドルするために必要なプラグイン
+    new VueLoaderPlugin(),
+  ],
+  module: {
+    rules: [
+      // .vue ファイルをビルドおよびバンドルする
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+      },
+      // .vue ファイル内の style をビルドおよびバンドルする
+      {
+        test: /\.vue\.css$/,
+        use: ['vue-style-loader', 'css-loader'],
+      },
+    ],
+  },
  resolve: {
    alias: {
+      // ESM (ECMAScript Modules) のバンドラー向けビルドを使用する
+      vue$: 'vue/dist/vue.esm-bundler.js',
      // .js ファイルや .vue ファイル内で @src エイリアスを使用して src ディレクトリを参照する
      '@src': path.resolve(__dirname, 'src/'),
    },
  },
};

適宜コメントを入れましたが、詳細な説明を省略したので、不明な点があれば以下の記事がおすすめです。

Webpack 5 を使った vue.js の環境構築と SFC の利用方法

こちらの記事は、単一ファイルコンポーネントを利用する場合の Vue.js の開発環境構築手順が丁寧に説明されていて分かりやすかったです。

なお、Babel や Jest などの他のライブラリについても同じ手順で設定可能なので、ぜひやってみてください。

環境構築は以上となります。
以降は、この環境における単一ファイルコンポーネントの利用方法について簡単に説明していきます。

おまけ. アプリケーション作成

まず、ルートプロジェクトの src/main/java/ ディレクトリ配下の適切なパッケージにコントローラークラスを定義してください。

IndexController.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {
    @GetMapping
    public String display() {
        return "index";
    }
}

同じくルートプロジェクトの src/main/resources/templates/ ディレクトリ直下に index.html ファイルを作成します。

index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <div id="main"><example/></div>

    <script th:src="@{/dist/js/vendor.bundle.js}"></script>
    <script th:src="@{/dist/js/index.bundle.js}"></script>
</body>
</html>

以降は frontend サブプロジェクトでの作業です。

まず、エントリーポイントとなる JavaScript ファイルを作成します。
frontend ディレクトリ直下に src/main/ ディレクトリを作成し、以下のような内容の index.js ファイルを作成してください。

index.js
import { createApp } from 'vue';
import example from '@src/main/components/example.vue';

createApp({
  components: {
    example: example,
  },
}).mount('#main');

つぎに、このファイルでインポートされている単一ファイルコンポーネントを作成します。
同じく src/main/ ディレクトリの直下に components/ ディレクトリを作成し、以下のような example.vue ファイルを作成してください。

example.vue
<template>
  <div class="message">{{ message }}</div>
</template>

<script>
export default {
  data () {
    return {
      message: 'Hello Vue Component'
    }
  }
}
</script>

<style scoped>
.message {
  font-weight: bold;
}
</style>

以上です。

最終的なディレクトリ構成は以下の通りです。

2024-02-10-170744.png

それでは、ルートプロジェクトにおいて、以下のコマンドを実行してアプリケーションを実行しましょう。

$ ./gradlew bootRun

Node.js プロジェクトのビルドに成功すると、ルートプロジェクトの src/main/resources/static/dist/js/ ディレクトリに index.bundle.jsvendor.bundle.js が生成されているはずです。

2024-02-10-183547.png

ブラウザで http://localhost:8080 にアクセスして、以下のように表示されれば成功です。

2024-02-10-184037.png

さいごに

今回はシンプルな構成、簡単な環境構築にこだわってみましたが、パフォーマンスはどうなんだろうとか、ファイルサイズは削減できるところはないだろうかとか、悩みだしたらきりがないですね。
メリデメを考慮して環境構築ができるようになりたいものです。

最後までお読みいただき、ありがとうございました!

追記

SPA バージョンの記事も書きました!

よろしければ、こちらもどうぞ。

4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9