😸

Vue3 + Express + TypeScript でウェブサイトを作る

2021/06/15に公開

こんにちわ、こんばんわ。
前職で無能・無能と言われたのが地味に堪えているさとかずです。
ずいぶんひさしぶりの投稿です。

「無能 + 無職 + ボッチ + 緊急事態宣言に在住 = ひきこもり」
という式が成立したため一日在宅、256歩で生活中です。
(そろそろ転職したい!誰か雇ってください。 ご連絡お待ちしております。)

転職活動のためウェブ技術をいろいろ試しています。
(意識高い(系の)人たち、後先考えずにとりあえず発表とか
下位互換無視したバージョンアップとか、すぐによく似た機能を新しく発表するとか止めてくんね?)

あれこれ試していて、ふと疑問いました。
「node.jsでフロントエンドからサーバーサイドまで一緒に作らないの?」

無能系ボッチは、ウェブサービス一つにつき
一つのディレクトリで取り組まないとできないのです。

そこで「Vue3 + express + Typescript」を利用して
無能でもできる「ハロー, XXさん」を作ってみたいと思います。

お断り
ほとんどがOPTiM様が掲載しているブログを参考にしています。
詳しいことはぜひそちらをごらんください。

参考サイト:
https://tech-blog.optim.co.jp/entry/2020/08/12/090000

環境

  • OS ZorinOS (Ubuntu系)
  • Node.js (v16.2.0)

作成手順

vue-cliを利用してプロジェクトの作成

$ npm -g install @vue/cli
$ cd /path/to/your/favorite/
$ vue create awesome-project

vue create を実行すると対話形式で設定を行っていきます。
基本的にお好みでいいとおもいます。

? Please pick a preset: 
Default ([Vue 2] babel, eslint) 
Default (Vue 3) ([Vue 3] babel, eslint) 
❯ Manually select features 

? Check the features needed for your project: 
◉ Choose Vue version
◉ Babel
◉ TypeScript
◯ Progressive Web App (PWA) Support
◉ Router
◉ Vuex
◯ CSS Pre-processors
◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing

? Choose a version of Vue.js that you want to start the project with 
2.x 
❯ 3.x 

? Use class-style component syntax? (y/N) N

? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n) Y

? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) Y

? Pick a linter / formatter config: 
❯ ESLint with error prevention only 
ESLint + Airbnb config 
ESLint + Standard config 
ESLint + Prettier 

? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Lint on save
◯ Lint and fix on commit

? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
❯ In dedicated config files 
In package.json 

? Save this as a preset for future projects? (y/N) N
...

インストール後の確認

$ cd awesome-project
$ yarn serve

http://localhost:8080/ に下図のように表示できていることを確認する。

確認後、terminalに戻り Ctrl + c で終了する

@types/nodeの追加

エディタの補完機能を利用するために導入します

yarn add --dev @types/node

srcディレクトリの修正

フロントとサーバーサイド側を両方管理するために、srcディレクトリにそれぞれfrontend, backendを作成する

$ cd /path/to/your/favorite/awesome-project
$ mv src frontend
$ mv pubic frontend
$ mkdir -p src/backend
$ mv frontend src

vue.config.jsの作成

awesome-project/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.jsonの修正

@@ -17,7 +17,7 @@
     ],
     "paths": {
       "@/*": [
-        "src/*"
+        "src/frontend/*"
       ]
     },
     "lib": [
@@ -28,11 +28,9 @@
     ]
   },
   "include": [
-    "src/**/*.ts",
-    "src/**/*.tsx",
-    "src/**/*.vue",
-    "tests/**/*.ts",
-    "tests/**/*.tsx"
+    "src/frontend/*.ts",
+    "src/frontend/*.tsx",
+    "src/frontend/*.vue"
   ],
   "exclude": [
     "node_modules"

vueのテストとビルド

$ yarn serve

http://localhost:8080/ が表示できていることを確認後、ビルドする

$ yarn build

awesome-project/dist/public ディレクトリにビルドした内容があることを確認する

expressの追加

yarn add --dev  @types/compression @types/express-session @types/lusca @types/errorhandler @types/express @types/dotenv
yarn add compression express-session lusca errorhandler express dotenv

tsconfig.backend.jsonの作成

awesome-project/tsconfig.backend.jsonを作成する

{
    "compilerOptions": {
      "module": "commonjs",
      "esModuleInterop": true,
      "allowSyntheticDefaultImports": true,
      "target": "es6",
      "noImplicitAny": true,
      "moduleResolution": "node",
      "sourceMap": true,
      "outDir": "dist",
      "baseUrl": ".",
      "paths": {
        "*": [
          "node_modules/*",
          "src/backend/types/*"
        ]
      }
    },
    "include": [
      "src/backend/**/*.ts"
    ],
    "exclude": [
      "node_modules"
    ]
  }

webpackのインストール

参考サイトには記載がないのだけれども、yarn serveしたら怒られたため導入しています。

yarn add --dev webpack @types/webpack

express用のserver.tsを作成する

awesome-project/src/backend/server.tsを作成する
envなどは使用せずにserver.tsに直接書き込んでいきます

import express from 'express'
import router from "./router";
import path from 'path'

const app = express()

app.use(router)

app.use(express.static(path.join(__dirname, "public")))

export default app.listen(3000, () => {
    console.log("App is running at http://localhost:3000")
})

express用のrouter.tsを作成する

awesome-project/src/backend/router.tsを作成する

import express from "express";


const router: express.Router = express.Router()

router.get('/api/version', (req: express.Request, res: express.Response) => {
    console.log("/api/version")
    res.json({version: '0.0.1'})
})


export default router

expressをビルドする

$ tsc -b tsconfig.backend.json

成功すると awesome-project/dist/server.js が作成されます。

expressを実行する

$ node ./dist/server.js

http://localhost:3000/api/version/ にアクセスしてjsonが出てくることを確認する
http://localhost:3000/ にアクセスしてvueの初期表示ができていることを確認する

package.jsonの編集

yarn で簡単に実行できるようにpackage.jsonの修正を行う

   "scripts": {
-    "serve": "vue-cli-service serve",
-    "build": "vue-cli-service build",
-    "lint": "vue-cli-service lint",
-    "build:backend": "tsc -b tsconfig.backend.json"
+    "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"

今後、ソースを更新したときは下のように実行する

$ yarn build:backend
$ yarn build:frontend
$ yarn serve

注意 webpack yarn build:frontendでエラーが出る場合。

package.jsonのwebpackのバージョンを変更します。

"webpack": "^4.46.0"

express に apiの内容を追加する

src/backend/router.tsを編集する

import express, {Express} from "express";
import bodyParser from 'body-parser'

const router: express.Router = express.Router()

router.get('/api/version', (req: express.Request, res: express.Response) => {
    console.log("/api/version")
    res.json({version: '0.0.1'})
})

router.post('/api/hello', (req, res) => {
    res.send(`ハロー、 ${req.body.userName}さん`)
})

export default router

curlでPOSTのResponseを確認する

$ yarn build:backend && yarn serve:backend

# 別のterminalなどで
$ curl http://localhost:3000/api/hello/ -X POST -H "Content-Type:application/json" -d '{"userName":"鈴木一郎"}'
# ハロー、 鈴木一郎さん とレスポンスされる

axiosを追加する

frontendでajaxを簡単に使用できるようにaxiosを追加します。

yarn add axios

src/frontend/components/HelloWorld.vueを修正する

ようやくHello部分です。
名前を入力するテキストフィールドとボタンを追加します。
v-modelやv-onなどが分からない場合は、Vue3で調べてください

https://v3.ja.vuejs.org/guide/forms.html#基本的な使い方

https://v3.ja.vuejs.org/guide/events.html#イベントの購読

@@ -32,15 +32,33 @@
       <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
     </ul>
   </div>
+  <form>
+    <input type="text" v-model="data.userName" />
+    <button v-on:click.prevent="hello">挨拶</button>
+  </form>
 </template>
 
 <script lang="ts">
 import { defineComponent, reactive } from 'vue';
+import axios from "axios";
 
 export default defineComponent({
   name: 'HelloWorld',
   props: {
     msg: String,
+  },
+  setup() {
+    const data = reactive({userName: ""})
+
+    const hello = async () => {
+      const res = await axios.post("http://localhost:3000/api/hello", {userName: data.userName})
+      alert(res.data)
+    }
+
+    return {
+      data,
+      hello,
+    }
   }
 });
 </script>

最終的なテストを行う

 $ yarn build:frontend && yarn build:backend && yarn serve

ブラウザでhttp://localhost:3000/にアクセスして、テキストフィールドに「さとかず」と入力し、挨拶ボタンをクリックする
下図のようなアラートが表示していることを確認する

動作確認を行いながらの記事作成ですが、少しでもご参考になればと思います。
それでは。

Discussion