Vue.jsのSPAをFlaskのバックエンドで支えて、Herokuで動かす！

# 前提

## 開発環境

- mac OS X
- Python 3.7.1
- pip 19.3.1
- Pipenv, version 2018.11.26　(インストール：`python -m pip install pipenv`)
- Flask
- Node.js v10.16.0
- Vue.js (@vue/cli v4.1.2)
- Herokuのアカウント、あるよ。

##ディレクトリ構成の目標

```
[approute] 
   |-- [server] -- サーバーサイド(Flask app)
   |-- [client] -- クライアントサイド(Vue app)
   `-- サーバー起動にまつわる設定ファイル
```

#サーバーサイドの準備

##仮想空間の作成

```
$ pipenv install flask
```

→プロジェクト用の仮想空間&[Pipfile][Pipfile.lock]ファイルが作成される

```
[approute]
   |-- Pipfile
   `-- Pipfile.lock


(pc)Users/xxxxxx/virtualenvs/xxxxxxx
```

##Flask appのシンプルな内容のコードを取り急ぎ置いてみる

HOME URLにやってきたら「Hello world」って返すだけ。

```python:approute/server/main.py

from flask import Flask
app = Flask(__name__)
 
@app.route('/')
def hello():
    hello = "Hello world"
    return hello
 
if __name__ == "__main__":
    app.run(debug=True)
```

##仮想空間で実行するscriptを作成

packageは仮想空間にあるので、そちらを実行するようなスクリプトを作成

```text:approute/Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
flask = "*"

[requires]
python_version = "3.7"

[scripts]                          //←追記
start = "python server/main.py"    //←追記
```

`$ pipenv run start` で実行できるようになる


#Herokuにdeploy

## gunicornをインストール

HerokuのPythonサポートはgunicornというWEBサーバーを通して行なっている様です。

> https://devcenter.heroku.com/articles/python-gunicorn

ということで　`$ pipenv install gunicorn`

```text:approute/Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
flask = "*"
gunicorn = "*"　　 //←ここが追記された

[requires]
python_version = "3.7"

[scripts]
start = "python server/main.py"
```

そして、実行スクリプトを記載。`server/main.py`のappを実行するには、↓のように書く

```text:approute/Procfile
web: gunicorn server.main:app --log-file -
```

一応、ローカルでも`$ gunicorn server.main:app`で実行確認してみる

##Herokuでapp作成

Heroku( https://jp.heroku.com/ ）でアカウントを作成して、appも作成。
そしたらdeploy手順が掲載されているので、順次実行していく。

```
$ heroku login  // Herokuにログイン
$ cd approute　//作業ルートに移動
$ git init　　　//git初期化
$ heroku git:remote -a hogehoge　　　//Herokuにリモートレポジトリ
$ git add .　　
$ git commit -am "make it better"  //gitにコミット
$ git push heroku master  //Herokuにpush
$ heroku open //デプロイしたアプリを開く
```

Herokuにデプロイしたappから「hello world」言われました。
世界が開けたようです。よかったよかった。

#クライアントアプリを作成

##Vue UIでプロジェクトを作成

Vue UI、使いやすいので活用しようと思います。


![z.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165366/42145a0e-6941-8853-fc5a-65ef646fe81c.png)


![Y.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165366/311539ba-8e87-306c-0387-5a70981d376c.png)

↑今回はアプリルートのgit使っているのでoffります。

これ以降のプリセットとかはとりあえず好きにすればいいと思う。
SPAなので、Routerは必須。

そしたら、しばらくダウンロードとかで待つと。。。
できあがるので、↓の画面から実行スクリプトも実行できますよと。

![x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165366/238e3934-3018-bde4-8e2f-9f121e78be78.png)


#SPA

##クライアントサイドでSPA作る

Vue UIの「プロジェクト設置＞Vue CLI＞アセットディレクトリ」からでも、
主導で設定ファイルを作ってでもいいので、staticファイルの書き出し先をstaticに設定。

```vue:approute/client/vue.config.js
module.exports = {
  assetsDir: 'static'
}
```

で、Vue UIでも、termimnalでもいいから、`$ npm run build`を実行→appがコンパイルされる

```
[approute] 
   |-- [client] 
      |--[dist]　　　//←生成される
         |--[static]　　　//←jsとか書き出される
         `--index.html　　　//←rootのHTMLができる
      |--[public]
      |--[src]
      |--vue.config.js
      `--その他設定ファイル
 
```

##サーバーサイドでクライアントアプリをテンプレートとして設定

結論として、こんな感じに書き換えます。

```python:approute/server/main.py
import os
from flask import Flask, render_template

app = Flask(__name__, static_folder='../client/dist/static', template_folder='../client/dist')

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
    return render_template('index.html')


if __name__ == "__main__":
    app.run()
```

ちっと分解。

```python
from flask import Flask, render_template
（略）
    return render_template('index.html')
```

↑Flaskのrendar_templateを追加しまして、Vueのapp側で書き出したルートファイルである[index.html]をセットします。


```python
app = Flask(__name__, static_folder='../client/dist/static', template_folder='../client/dist')
```

↑Vueのapp側で書き出したstaticファイルの場所をこのファイルから見た相対パスで書きます。


```python
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
```

↑どんなURLパスでやってきても、こちらで受け止めます、な感じ。

`pipenv run start` →　お、世界に色が宿りました。

## 改めて、Herokuにpush

```
$ git add .　　
$ git commit -am "color from vue"
$ git push heroku master
$ heroku open
```

してみたら、、
500 Internal Server Error

```
jinja2.exceptions.TemplateNotFound: index.html
```

とのことで、ローカルでは見つかっていたVueによって生成されたindex.htmlが見つからない...ってことですかね？
世界が闇に包まれました。

#Herokuにdeploy・改

## 複数のbuildpackに対応する

すでに、pipfileによって、自動的にpythonサーバーが起動する。
そこに、node.jsサーバーもたちあげたい。

https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app

`$ heroku buildpacks:add --index 1 heroku/nodejs`

```
$ heroku buildpacks
=== xxx Buildpack URLs
1. heroku/nodejs
2. heroku/python
```

で、このまま起動しても

```
remote:  !     The 'heroku/nodejs' buildpack is set on this application, but was
remote:  !     unable to detect a Node.js codebase.
remote:  !         
remote:  !     A Node.js app on Heroku requires a 'package.json' at the root of
remote:  !     the directory structure.
```

と言われるわけで、package.jsonをルートに持ってこいと言われます。

## Vue.jsのアプリのディレクトリ構造を変更する

いったん書き出していた[dist]ディレクトリを削除して、[client]ディレクトリの中身をまるっとルートに移動させる

```
[approute] 
   ===Flask===
   |--[server] -- main.py 
   |--Pipfile
   |--Procfile
   |--その他設定ファイル
   ===Vue.js===
   |--[public]
   |--[src]
   |--package.js
   |--babel.config.js
   |--vue.config.js
   `--その他設定ファイル
 
```

それにともなって、書き換え

```python:approute/server/main.py
app = Flask(__name__, static_folder='../dist/static', template_folder='../dist')
```

Herokuにpushしてみたら、動いた！世界に光が戻りました。
ということで、このベースの世界から発展開発をしていきたいと思います。


# 結論

とりあえず、やったことを書いてみましたが。。
あったんだよね、先人の知恵
https://github.com/gtalarico/flask-vuejs-template
https://github.com/oleg-agapov/flask-vue-spa

なんか、ディレクトリ構成がね〜。
vue.config.jsとか設定して、１段掘れないかしら。


#参考

[FlaskとVue.jsでSPA Webアプリ開発](https://qiita.com/y-tsutsu/items/67f71fc8430a199a3efd)
[Heroku 複数ビルドパックの使い方](https://qiita.com/mikakane/items/51846d7d4e1af0d76a37)