Vue.js + Express + Typescript を同一リポジトリで開発する方法

こんにちは。プラットフォーム技術戦略室の青木です。

普段はLaravelやクラウドネイティブインフラストラクチャ、IoTなどの記事を執筆しています。 今回もLaravelの記事同様に、導入編となっています。 Express + Vue.jsの参考文献はあれど、Typescriptと併用したものが少なかったので私なりに作った連携方法を執筆したいと思います。

以前のLaravelの記事となります
tech-blog.optim.co.jp

サマリ

  • Vue.js + Express を利用した開発基盤を作る
  • フロントエンドは tsconfig.json と vue.config.json を。
  • バックエンドは tsconfig.backend.json (ファイル名は任意) をいい感じに変更すれば実装できる
  • 実装したものはこちら👇
    • github.com
    • このリポジトリをCloneして利用すれば簡単に実装できる

そもそも同一リポジトリで管理する理由ってなに?

厳密に言えば、同一リポジトリでなくてもいいです。

Vue.js [フロントエンド] を使いながらも Express [バックエンド] とセッション*1管理をしたい場合、Express側が読み取る public ディレクトリの中に Vue.js ビルド済ファイルを出力して表示する必要が出てきます。

同一リポジトリであれば Vue.js を public ディレクトリにビルドし、別リポジトリであればサブモジュールなどで管理する方法もあります。

今回は同一リポジトリで実装する際にどのような設定をすればいいかを紹介します。

手順

  • 事前にディレクトリ構成を決める
  • Vue.js をはじめる
  • Express をはじめる
  • 設定を変更する(Vue.js + Express 連携)
  • 完了!

事前にディレクトリ構成を決める

任意でディレクトリ構成を決めます。プロジェクトに応じてディレクトリを決めてください。 今回はフロントエンドとバックエンドを src の中に各ディレクトリに分割して設置しました。

設定ファイルをプロジェクト直下においています。

この構成を以下の手順にしたがって順に作成していきます。

SampleProject
├ src
│ ├ frontend
│ │ └ (Vue.js Project)
│ └ backend
│   └ (Express Project)
├ tsconfig.json
├ tsconfig.backend.json
└ vue.config.json

Vue.js をはじめる

新規プロジェクト作成

Vue CLI を使ってプロジェクトを作成します。

cli.vuejs.org

$ vue create optim-techblog-vueexpressts-sample

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200811/20200811124353.png

プリセット選択時に マニュアル を選択し、 TypeScript に変更しましょう。 後はプロジェクトに応じて変更してください。

実行後は以下のファイルが自動生成されると思います。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200811/20200811124350.png

現時点で動作するかだけは確認しておきましょう。

$ npm run serve
Version: typescript 3.9.7, tslint 5.20.1
Time: 1449ms

  App running at:
  - Local:   http://localhost:8080/
  - Network: ********

いくつかのWarningが出ますが今回は無視します

ディレクトリ構成の変更

ディレクトリ構成を変更した場合、Vue CLIのデフォルトファイル参照場所から外れるためビルドが通らなくなります。

このような場合 vue.config.js を作成し、設定を変更することで動作するようになります。

  • src/frontend を作成
  • publicsrc/frontend/public
  • src 直下のファイルを全て src/frontend
  • vue.config.js を作成
.
├── README.md
├── babel.config.js
├── package.json
├── src
│   └── frontend
│       ├── App.vue
│       ├── assets
│       │   └── logo.png
│       ├── components
│       │   └── HelloWorld.vue
│       ├── main.ts
│       ├── public
│       │   ├── favicon.ico
│       │   └── index.html
│       ├── router
│       │   └── index.ts
│       ├── shims-tsx.d.ts
│       ├── shims-vue.d.ts
│       ├── store
│       │   └── index.ts
│       └── views
│           ├── About.vue
│           └── Home.vue
├── tsconfig.json
├── tslint.json
├── vue.config.js
└── yarn.lock

vue.config.js

const path = require('path')

module.exports = {
    configureWebpack: {
        resolve: {
            alias: {
                '@': path.join(__dirname, '/src/frontend')
            }
        }
    },
    outputDir: 'dist/public',
    pages: {
        index: {
            entry: 'src/frontend/main.ts',
            template: 'src/frontend/public/index.html',
        }
    }
}

これに応じて、tsconfig.js のコンパイル対象も変更しておきます。

tsconfig.js

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env"
    ],
    "paths": {
      "@/*": [
-        "src/*"
+        "src/frontend/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
-    "src/**/*.ts",
-    "src/**/*.tsx",
-    "src/**/*.vue",
-    "tests/**/*.ts",
-    "tests/**/*.tsx"
+    "src/frontend/**/*.ts",
+    "src/frontend/**/*.tsx",
+    "src/frontend/**/*.vue"
  ],
  "exclude": [
    "node_modules"
  ]
}

この状態で再度動作するか検証します。

$ npm run serve
Version: typescript 3.9.7, tslint 5.20.1
Time: 1449ms

  App running at:
  - Local:   http://localhost:8080/
  - Network: ********

Express をはじめる

Express は自前で構築していきます。構成は任意で構いません。

パッケージのインストール

必要なパッケージをインストールします。

$ npm i @types/compression @types/express-session @types/lusca @types/errorhandler @types/express @types/dotenv compression express-session lusca errorhandler express dotenv

メインプログラムを作成

src/backend に追加していきます。 ソースコードが長いので、Githubより参照してください。

github.com

設定ファイルの変更

tsconfig.backend.json

下記の部分が backend のディレクトリとなります。任意で変更した方は間違えないようにしてください。

"include": [
      "src/backend/**/*.ts"
    ],

Outputのディレクトリを dist としています。こちらも任意ですが、 Vue.js と連携する部分でもありますので確認してください。

"outDir": "dist",

package.json

ビルドするためのコマンドを記述します。

"scripts": {
    // ...
    "build:backend": "tsc -b tsconfig.backend.json",
    // ...
}

ビルド

$ npm run build:backend

確認

サーバを立てます。

$ node ./dist/server.js
  App is running at http://localhost:3000 in development mode
  Press CTRL-C to stop

/api/version のAPIを生やしておいたので、結果が帰ってくるか確認してみましょう

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200811/20200811124341.png

設定を変更する (Vue.js + Express 連携)

ほとんど完成していますので、後は微調整です。

package.json

-    "serve": "vue-cli-service serve",
-    "build": "vue-cli-service build",
-    "lint": "vue-cli-service lint",
+    "serve": "node ./dist/server.js",
+    "watch:frontend": "vue-cli-service build --watch",
+    "build:frontend": "vue-cli-service build",
    "watch:backend": "tsc-watch -b tsconfig.backend.json",
    "build:backend": "tsc -b tsconfig.backend.json",
    "serve:backend": "nodemon ./dist/server.js"

vue-cli-service build で出力されるのは dist/public となり、tsc -b tsconfig.backend.json で出力されるのは dist になります。

ですので、

  1. npm run build:backend
  2. npm run build:frontend

の順番でビルドすることで、 Express が読み込む publicディレクトリがVue.jsのビルドによって出力され、連携できます。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200811/20200811124336.png

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200811/20200811124339.png

完了!

サーバを立てて、Vue.js が読み込まれているか確認してみます。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200811/20200811124348.png

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20200811/20200811124344.png

代替手段

yarnを使う方法

yarn に実装されている Workspace を利用することで、似たようなことを実現可能になります。

classic.yarnpkg.com

git Submodule を使う方法

git Submoduleを利用し、別リポジトリのものを読み込んで実装することも可能です。 実運用や大人数の開発の際は複数のリポジトリに分けることもできます。

git-scm.com

さいごに

Express側に情報を保持したい、セッションを利用したいがフロントはVue.jsを使いたいというニーズを満たすプロジェクトの開始方法を記事にしました。
これがベストプラクティスであるかどうかはプロジェクトの規模や構成によって変わるかと思いますが、 tsconfig.json などの記述方法は変わりません。是非参考にして頂いてプロジェクトを円滑に進めて頂ければ幸いです。


OPTiMでは様々な分野で活躍できるエンジニアを募集しております。

興味がある方は是非弊社採用ページにてご覧ください!

www.optim.co.jp

*1:cookieなどの管理も含む