Vue.js Typescript Express で開発環境を作ってみる

# 内容

Vue.js + express + Typescriptで、何かプロダクト開発をする際のベースとなる環境を作ってみます。
expressはAPIとして利用し、バックエンドとして使用する想定です。
ベースということで今回は、必要最低限（下記）の設定に留めます。

- ホットリロードでファイル保存時に自動反映し、再ビルドや再起動が不要である。
- 1コマンドで起動が可能である。
- 保存時自動フォーマット

## 1. vue + typescript

### @vue/cli をグローバルインストール

```
npm install -g @vue/cli
```

### vue雛形生成
```
vue create express-vue-typescript
```

```
Please pick a preset: 
-> Manually select features

Check the features needed for your project:
-> [Babel, Linter/Formatter, TypeScript] + お好み を選択

Use class-style component syntax? (Y/n) 
-> Yes

Use Babel alongside TypeScript ?
-> Yes

Use history mode for router? (Requires proper server setup for index fallback in production)
-> Yes

Pick a linter / formatter config: 
-> Prettier

Pick additional lint features: 
-> Lint on save

Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? 
-> In dedicated config files

Save this as a preset for future projects? No
-> No
```

## 2. express
### express install 
```
npm install express @types/express
```
### 起動ファイル作成 
```express-vue-typescript/src/server/app.ts
import express from 'express'
import path from 'path'
import config from 'config'

const port = 8080
const app = express()
app.use(express.static('dist'))

// APIとして利用するパスは随時追加
// app.get('/api/doSomething', doSomething)

app.get(/.*/, (req: express.Request, res: express.Response) => {
  res.sendFile(path.resolve('dist/index.html'))
})
app.listen(port, () => console.log(`Listening at http://localhost:${port}`))
```

## 3. vue.config.jsの追加 

```express-vue-typescript/vue.congig.js

module.exports = {
  devServer: {
    port: 8081,
    // localhostでvueからexpressにAPIリクエストを送信する為の設定
    proxy: 'http://localhost:8080' 
  },
  configureWebpack: {
    devtool: 'source-map'
  }
}
```

## 4. ホットリロード開発環境構築
### nodemon ts-node install
```
npm install --save-dev nodemon ts-node
```
### nodemon用configファイル追加
```express-vue-typescript/src/nodemon.json
{
  "watch": ["src/server"],
  "ext": "ts",
  "exec": "ts-node -O '{\"module\": \"commonjs\"}' src/server/app.ts"
}
```
### 起動コマンド追加
```express-vue-typescript/package.json
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "dev": "set NODE_ENV=development && nodemon -L >&2 | npm run serve",
    "prod": "npm run build && nodemon -L",
  }
```


## 5. eslintの設定更新
必要があれば設定の追加等を行う。以下設定例

```express-vue-typescript/eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true
  },
  'extends': [
    'plugin:vue/essential',
    '@vue/prettier',
    '@vue/typescript'
  ],
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
    "no-var": "error",
    "prefer-const": "error",
    "object-shorthand": "error",
    "prefer-arrow-callback": "error",
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        "argsIgnorePattern": "^_"
      }
    ],
    'prettier/prettier': [
      'error',
      {
        'singleQuote': true,
        'semi': false
      }
    ]
  }
};
```

### VSCodeで保存時自動フォーマット
下記ファイルを配置

```express-vue-typescript/.vscode/settings.json
{
  "eslint.autoFixOnSave": true,
  "eslint.validate": [
    "javascript",
    {
      "language": "typescript",
      "autoFix": true
    },
    {
      "language": "vue",
      "autoFix": true
    }
  ]
}
```

### VSCodeでexpressのデバッグ
下記ファイルを配置

```express-vue-typescript/.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "DEBUG express",
      "args": [
        "${workspaceFolder}/src/server/app.ts"
      ],
      "runtimeArgs": ["-r", "ts-node/register"],
			"cwd": "${workspaceRoot}",
			"protocol": "inspector",
			"internalConsoleOptions": "openOnSessionStart",
			"env": {
        "TS_NODE_COMPILER_OPTIONS": "{\"module\": \"commonjs\"}",
				"TS_NODE_IGNORE": "false"
			}
    }
  ]
}
```

## 開発手順
```
# vue-dev-serverとexpressを同時に起動
npm run dev
```
※ [5. eslintの設定更新](#5-eslintの設定更新) でeslintの設定を更新している場合はエラーが表示される可能性があります。

コマンド実行後、ファイルを保存することでホットリロードされ、下記URLで動作確認が可能
http://localhost:8081 [vue-dev-server]

※ axios等の利用によるvueプロジェクト内でのAPI接続はプロキシされる

```javascript
const doSomething = () => axios.get('/api/doSomething/')
// http://localhost:8081/api/doSomething -> http://localhost:8080/api/doSomething
```

## 本番環境想定

```
# vueプロジェクトのbuildとexpressを起動
npm run prod
```
http://localhost:8080 [express-server]