🌸

SpringBoot で Vue3 を組み込む事始め① - SpringBoot から Vue を呼び出す

2023/06/29に公開

前書き

既存のSpringBootのViewテンプレートをFreemarkerで画面を作ってましたが、いい加減メンテナンスが辛くなってきた(ドキュメントがThymeleafに比べて少ない等)し、流行ってるSPAもやってみたいし、いっそVueに置き換えてみようか、というチャレンジ内容です。
 そして、使ってるユーザも限られてるし、Apache httpd×Tomcatで動いてるサーバにさらにWebサーバ建てたりするのは面倒すぎるので、SpringBootアプリの中に含められるなら含めようという魂胆で始まりました。バラバラになるとデプロイめんどくさいですしね。
という感じで始めていきたいと思います。


補足:Java/SpringBootにはある程度慣れている前提で記述しています。

環境

  • WSL2 / Ubuntu
  • Java11
  • SpringBoot2.7
  • tomcat10
  • Vue3
  • Vite
  • TypeScript

ソースコード

まっさらな状態から作ったソースコードは こちら

最終的な成果物

内包ではないtomcatを使用しているので、warファイルを生成

階層構造

springboot-vue3
├── bin
├── build
├── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   │       ├── static      -- ここにvueのビルドしたソースが格納される
│   │       └── templates   -- 今までのViewが入っているのを移行していく(最終的にはなくなる)
│   └── test
├── frontend
│   ├── README.md
│   ├── env.d.ts
│   ├── index.html
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   ├── src
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── tsconfig.vitest.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── gradle
│   └── wrapper
├── gradlew
├── gradlew.bat
├── build.gradle
├── settings.gradle
└── README.md

前準備

build.gradle
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

または

build.gradle
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'org.springframework.boot:spring-boot-starter-freemarker'

私の環境ではFreemarkerを使ってるので、後者のパターンで検証しています。

作業開始

Vue プロジェクトの作成

$ npm init vue@latest
Need to install the following packages:
  create-vue@3.7.1
Ok to proceed? (y) y

Vue.js - The Progressive JavaScript Framework

✔ Project name: … frontend
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes

Scaffolding project in /home/spring/mti-git/Tools/Smoothie/frontend...

Done. Now run:

  cd frontend
  npm install
  npm run format
  npm run dev

$ cd frontend/
$ npm install
$ npm run dev

以下の画面が出ます。

Ctl + C で起動停止で。

(おまけ)Prettier の設定

ここを参考に設定する。
とりあえず、末尾のセミコロンとシングルクォーテーションとアロー関数の()を省略しないのを有効にしたぐらい。
Next.jsにPrettierを導入して設定する

.prettierrc.json
{
  "$schema": "https://json.schemastore.org/prettierrc",
  "semi": true,
  "tabWidth": 2,
  "singleQuote": false,
  "printWidth": 100,
  "trailingComma": "none",
  "arrowParens": "always"
}

ビルド先の設定

https://github.com/yukiko-bass/springboot-vue3/blob/feature/01_initial/frontend/vite.config.ts

ちなみに、emptyOutDirをtrueにすると、outDirで指定したフォルダの中を全部消します。
初めから作る場合はtrueで良いと思いますが、既存のプロジェクトに入れて徐々に作り変えていくつもりなので、消すのは早すぎるため、ここではfalseに。

  • ビルド
$ npm run build

> frontend@0.0.0 build
> run-p type-check build-only


> frontend@0.0.0 type-check
> vue-tsc --noEmit -p tsconfig.vitest.json --composite false


> frontend@0.0.0 build-only
> vite build

vite v4.3.9 building for production...
✓ 48 modules transformed.
../src/main/resources/static/assets/logo-277e0e97.svg        0.28 kB │ gzip:  0.20 kB
../src/main/resources/static/index.html                      0.42 kB │ gzip:  0.28 kB
../src/main/resources/static/assets/AboutView-4d995ba2.css   0.09 kB │ gzip:  0.10 kB
../src/main/resources/static/assets/index-9f680dd7.css       4.20 kB │ gzip:  1.30 kB
../src/main/resources/static/assets/AboutView-37721347.js    0.23 kB │ gzip:  0.20 kB
../src/main/resources/static/assets/index-cb21adc8.js       85.38 kB │ gzip: 33.64 kB
✓ built in 987ms

ファイルが生成されていればOKです。

SpringBootからVueの画面が表示されるようにする

既存アプリでは以下のように / または /index のパスでリクエストが来た場合に、indexのViewを返すようにしていました。

MainController.java
@Controller
public class MainController {

	@RequestMapping(value = { "/", "/index" })
	@ResponseBody
	public ModelAndView index(ModelAndView mv) {
		mv.setViewName("index");
		return mv;
	}
}

VueはSPAで作成しているので、常に index.html を返す必要があるため、上記のMainControllerの記述をコメントアウトして、以下のVueController.javaを作成し、/ が来た場合はindex.htmlを返すようにします。

https://github.com/yukiko-bass/springboot-vue3/blob/feature/01_initial/src/main/java/jp/bass/springboot/vue/controller/VueController.java

SpringBootのプロジェクトを起動して、http://localhost:8080 にアクセスすると、Vueの画面が表示されればOKです。SpringBoot側でも見れるようになりました。

/ 以外のPathもVue画面に飛ばすようにする

このままだと『/』に来た時しかVueの画面が表示されないので、とりあえず全部Vueの画面に飛ばすようにします。

PathResourceResolver を継承して、SpaPageResourceResolver クラスを作成します。
resource がなかったらvueのindex.htmlを返すという処理をやってるくさいです。(適当)

https://github.com/yukiko-bass/springboot-vue3/blob/feature/01_initial/src/main/java/jp/bass/springboot/vue/config/SpaPageResourceResolver.java

WebMvcConfig で上記で作ったResolverを追加します。
全部のパスに対して処理するよ、って今のところしてます。
きっとここでapiのパスだったらというのが後ほど追加されるはず。

https://github.com/yukiko-bass/springboot-vue3/blob/feature/01_initial/src/main/java/jp/bass/springboot/vue/config/WebMvcConfig.java

java側にコードを追加できたら、起動して http://localhost:8080 にアクセスします。
このパスにいろいろ追加したりしてもvueの画面が表示されるはずです。
一部欠けたりしてるかもしれないですが、細かいことは気にしない。


とりあえず画面が表示できるようになったので、一区切りです。

Next

https://zenn.dev/yukiko_bass/articles/6033654959f1ee

参考

Spring Boot + Vue.js (SPA)アプリの構築方法

Discussion