SpringBoot で Vue3 を組み込む事始め① - SpringBoot から Vue を呼び出す
前書き
既存の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
前準備
- node & npm のLTS版バージョンアップ
- 既存のSpringbootプロジェクトを改修していく形式なので、手元にない場合は、SpringInitializr でプロジェクトを作成してください。
webとThymeleafかFreemarkerが含まれていれば大丈夫なはず。
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
または
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を導入して設定する
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": true,
"tabWidth": 2,
"singleQuote": false,
"printWidth": 100,
"trailingComma": "none",
"arrowParens": "always"
}
ビルド先の設定
ちなみに、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を返すようにしていました。
@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を返すようにします。
SpringBootのプロジェクトを起動して、http://localhost:8080 にアクセスすると、Vueの画面が表示されればOKです。SpringBoot側でも見れるようになりました。
/ 以外のPathもVue画面に飛ばすようにする
このままだと『/』に来た時しかVueの画面が表示されないので、とりあえず全部Vueの画面に飛ばすようにします。
PathResourceResolver を継承して、SpaPageResourceResolver クラスを作成します。
resource がなかったらvueのindex.htmlを返すという処理をやってるくさいです。(適当)
WebMvcConfig で上記で作ったResolverを追加します。
全部のパスに対して処理するよ、って今のところしてます。
きっとここでapiのパスだったらというのが後ほど追加されるはず。
java側にコードを追加できたら、起動して http://localhost:8080 にアクセスします。
このパスにいろいろ追加したりしてもvueの画面が表示されるはずです。
一部欠けたりしてるかもしれないですが、細かいことは気にしない。
とりあえず画面が表示できるようになったので、一区切りです。
Next
Discussion