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」って返すだけ。

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は仮想空間にあるので、そちらを実行するようなスクリプトを作成

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

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を実行するには、↓のように書く

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、使いやすいので活用しようと思います。

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

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

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

SPA

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

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

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
      `--その他設定ファイル

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

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

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()

ちっと分解。

from flask import Flask, render_template

    return render_template('index.html')

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

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

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

@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サーバーもたちあげたい。

$ 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
   `--その他設定ファイル

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

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とか設定して、1段掘れないかしら。

参考

FlaskとVue.jsでSPA Webアプリ開発
Heroku 複数ビルドパックの使い方