Vue.jsとFlaskでフルスタックなWebアプリの開発環境を構築 その1〜〜環境構築〜〜
- はじめに
- vue-cliで雛形のインストール(フロントエンド)
- サンプルページの追加
- ビルドディレクトリの変更
- Flaskのインストール(バックエンド)
- Flaskのサンプル作成
- 404ページの追加
- APIの追加
- 終わりに
はじめに
最近のWebアプリはReact.jsやVue.jsを使用したSPAが主流かと思いますが、データ取得のAPIを実装したりなど、サーバサイドの実装も必要になります。
自分はJavascriptに不慣れなのでサーバサイドはJavascriptではなくPythonを使いたかったので、Flaskと共存できる構成にしたかったのがきっかけです。
いろいろと探してみたらすでに先人がやっていました。
Full-stack single page application with Vue.js and Flask
Single Page Apps with Vue.js and Flask
以下のような構成で開発できるようにします。
app_dir ┗frontend // Vue.jsのプログラム ┗backend // Flaskのプログラム
英語記事なのと、それぞれの記事で若干やっていることが異なるため
それぞれを混ぜる形で環境を構築したので、備忘として自分の言葉でまとめておこうと思います。
vue-cliで雛形のインストール(フロントエンド)
以下コマンドでvue-cliをインストールできます。
$ npm install -g vue-cli
アプリの雛形をインストールします。
$ vue init webpack frontend ? Project name (frontend):[Enter] ? Project description (A Vue.js project):[Enter] ? Author (hogehoge <hogehogehogehoge@gmail.com>):[Enter] ? Vue build (Use arrow keys):[Enter] ❯ Runtime + Compiler: recommended for most users Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere ? Install vue-router? (Y/n):[Enter] ? Use ESLint to lint your code? (Y/n):[Enter] ? Pick an ESLint preset (Use arrow keys) ❯ Standard (https://github.com/standard/standard) Airbnb (https://github.com/airbnb/javascript) none (configure it yourself) ? Set up unit tests (Y/n):[Enter] ? Pick a test runner (Use arrow keys):[Enter] ❯ Jest Karma and Mocha none (configure it yourself) ? Setup e2e tests with Nightwatch? (Y/n):[Enter] ? Should we run `npm install` for you after the project has been created? (recommended) (Use arrow keys) ❯ Yes, use NPM Yes, use Yarn No, I will handle that myself vue-cli · Generated "frontend". # Installing project dependencies ... # ======================== (略) # Project initialization finished! # ======================== To get started: cd frontend npm run dev Documentation can be found at https://vuejs-templates.github.io/webpack $ cd frontend $ npm install # after installation $ npm run dev DONE Compiled successfully in 21497ms 12:42:34 I Your application is running here: http://localhost:8080
ブラウザでhttp://localhost:8080
を開いて以下の画面が表示されれば、アプリの雛形インストール完了です。
サンプルページの追加
Vue.jsの構成や説明はここでは記載しませんが、練習用にページを追加してみましょう。
frontend/src/components
にHome.vue
とAbout.vue
を追加します。
// Home.vue <template> <div> <p>Home page</p> </div> </template>
// About.vue <template> <div> <p>About</p> </div> </template>
また、frontend/src/router/index.js
を以下のように編集します。
import Vue from 'vue' import Router from 'vue-router' // もともと記載されていたものは削除orコメントアウト // import HelloWorld from '@/components/HelloWorld' // Vue.use(Router) // export default new Router({ // routes: [ // { // path: '/', // name: 'HelloWorld', // component: HelloWorld // } // ] // }) const routerOptions = [ { path: '/', component: 'Home' }, { path: '/about', component: 'About' } ] const routes = routerOptions.map(route => { return { ...route, component: () => import(`@/components/${route.component}.vue`) } }) Vue.use(Router) export default new Router({ routes, mode: 'history' })
このように編集したら再度ブラウザでlocalhost:8080
とlocalhost:8080/about
を開いてページが表示されたらOKです。
ビルドディレクトリの変更
npm run build
コマンドで立ち上がるのは開発用のサーバなので、実際にサービス提供する際はブラウザが解釈できる形にビルドする必要があります。
そのビルドした結果がどこに出力されるかはfrontend/config/index.js
に記載があります。
もともとの記載は、
build: { // Template for index.html index: path.resolve(__dirname, '../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../dist'), //////以下略////// }
となっており、frontend/dist
配下にビルドされたソースコードが出力される設定となっております。
このままだとFlaskでindex.htmlにアクセスするときにfrontend
配下を参照しなくてはならなくなってしまうので、管理上明確に分離するためにfrontend
の上の階層に出力されるようにします。
build: { // Template for index.html index: path.resolve(__dirname, '../../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../../dist'), //////以下略////// }
以下コマンドでビルドします。
$ npm run build
以下のような構成になります。
app_dir ┗frontend // Vue.jsのプログラム ┗dist // コンパイルされたVue.jsのプログラム
$ ls ../dist/
index.html static
Flaskのインストール(バックエンド)
バックエンドのプログラムには今回はFlaskを使用します。
元記事にはPythonの環境を整えるのにvirtualenvを使用していますが、僕はpyenv(pyenv-virtualenv)のほうが使い慣れているのでこちらを使います。
pyenvは以下コマンドでインストールできます。
$ brew install pyenv $ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile
以下コマンドで本アプリ用のPython3.6.1の環境を作ります。
$ pyenv install --list $ pyenv install 3.6.1 $ pyenv virtualenv 3.6.1 vue-flask $ pyenv versionos system 3.6.1 3.6.1/envs/vue-flask vue-flask
app_dir
に行き、以下のコマンドを実行します。
$ pyenv local vue-flask (vue-flask) $ # このようにプロンプトが変わったらOK
これで、app_dir
内に閉じたPython3.6.1の環境が作れました。
(この中でパッケージのインストールを行っても他の環境には影響ありません。)
以下コマンドでFlaskをインストールします。
$ pip install Flask
$ mkdir backend
Flaskのサンプル作成
いよいよFlaskでバックエンドのプログラムを作成していきます。
今回は以下のようなファイル構成にしようと思います。
app_dir ┗appserver.py ┗frontend/ ┗backend/ ┗api.py ┗application.py ┗config.py ┗router.py
それぞれのファイルの中身は以下です。
# appserver.py from backend.application import create_app from flask import render_template app = create_app() @app.route('/') def index(): return render_template("index.html") if __name__ == '__main__': app.run()
# application.py from flask import Flask def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') return app
# config.py class BaseConfig(object): DEBUG = True
ポイントは、application.py
において、
app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist")
を指定することです。
これを指定することで、Flaskのテンプレート参照先が./dist
配下になり、Vue.jsでビルドしたindex.html
が参照されることになります。
以下コマンドで、Flaskの開発用サーバが立ち上がります。
$ python appserver.py * Serving Flask app "FLASK-VUE" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 977-628-540
http://localhost:5000/
にアクセスすると、先程vue.jsで作成したホーム画面が表示されます。
しかし、vue.js側でルーティングを実装したはずの、http://localhost:5000/about
にアクセスしても、Not Foundとなってしまいます。
これは、Flask側ではルーティングをしていないにもかかわらず、Flask側で処理しようとしてしまっているためです。
そこで以下をappserver.py
に追記して、/
以下のパスをvue.jsでビルドしたindex.html
にリダイレクトするようにします。
# appserver.py from backend.application import create_app from flask import render_template app = create_app() #### 以下を追記 ### @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def catch_all(path): return render_template("index.html") ################# if __name__ == '__main__': app.run()
再度、http://localhost:5000/about
にアクセスするとAboutページが表示されます。
404ページの追加
これで、ブラウザ上のルーティングがvue.jsでビルドしたindex.html
にリダイレクトされるので、vue.js側で404ページも追加しておかなくてはなりません。
frontend/src/components
配下に以下NotFound.vue
を作成します。
// NotFound.vue <template> <div> <p>404 - Not Found</p> </div> </template>
また、frontend/src/router/index.js
に以下の通り、ページがない場合のパスを追記します。
const routerOptions = [ { path: '/', component: 'Home' }, { path: '/about', component: 'About' }, { path: '*', component: 'NotFound' } ]
http://localhost:8080/
配下の適当なパスにアクセスしてNotFoundのページが表示されればOKです。
npm run build
でビルドすればFlask側のサーバhttp://localhost:5000/
でも同様にNotFoundのページが表示されます。
APIの追加
さて、基本的な環境は整ってきたので、バックエンド側にAPIエンドポイントを追加していきたいと思います。
バックエンド(Flask)→APIエンドポイントの追加
フロントエンド(Vue.js)→APIアクセスし結果を描画
まずはFlask側にAPIに対するルーティングを追加します。
index.html
に対するルーティングと同様に、そのままappserver.py
に記載しても良いのですが、APIのルーティングは別ファイルで管理したいので、FlaskのBluprintを使用します。
backend
配下にapi.py
というファイルを作成します。
from flask import Blueprint, jsonify, request from random import * api = Blueprint('api', __name__) @api.route('/hello/<string:name>/') def say_hello(name): response = { 'msg': "Hello {}".format(name) } return jsonify(response) @api.route('/random') def random_number(): response = { 'randomNumber': randint(1, 100) } return jsonify(response)
今回は試しに名前に対してHelloのメッセージを返すAPIと乱数を返すAPIを作成しました。
backend/application.py
にてこのAPIのルーティングを記載したBlueprintファイルをimportします。
# application.py from flask import Flask def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') ##### ここを追記 ##### from backend.api import api app.register_blueprint(api, url_prefix="/api") #################### return app
url_prefix="/api"
を指定しているので、ここで記載するルーティングはすべて/api/
配下のルーティングとなります。
http://localhost:5000/api/random
にアクセスすると以下の結果
{ "randomNumber": 38 }
http://localhost:5000/api/hello/hoge
にアクセスすると以下の結果
{ "msg": "Hello hoge" }
上記のような結果になればOKです。
次にfrontend
配下のVue.jsのプログラム側でこのAPIにリクエストをし、画面表示させたいと思います。
まずは、Vue.jsでajaxを利用するために、axiosをインストールします。
$ cd frontend $ npm install --save axios
frontend/src/components/Home.vue
を以下のように書き換えます。
// Home.vue <template> <div> <p>Home page</p> <p>Random number from backend: {{ randomNumber }}</p> <button @click="getRandom">New random number</button> </div> </template> <script> import axios from 'axios' export default { data () { return { randomNumber: 0 } }, methods: { getRandom () { const path = 'http://localhost:5000/api/random' axios.get(path) .then(response => { this.randomNumber = response.data.randomNumber }) .catch(error => { console.log(error) }) } }, created () { this.getRandom() } } </script>
バックエンドのFlask側では、デフォルトでは他のサーバからのリクエストを受け付ける設定になっていないため、フロントエンドのVue.jsからのAjaxのリクエストを受け入れるようにします。
まずは以下コマンドでCORSをインストールします。
$ pip install -U flask-cors
backend/application.py
を以下のように書き換えます。
# application.py from flask import Flask from flask_cors import CORS # ここを追記 def create_app(app_name='FLASK-VUE'): app = Flask(app_name, static_folder = "./dist/static", template_folder = "./dist") app.config.from_object('backend.config.BaseConfig') from backend.api import api app.register_blueprint(api, url_prefix="/api") # ここを追記 cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) return app
これでhttp://localhost:8080/
アクセスしたときにAPIから取得した乱数を表示することができるようになったのですが、
もし静的なファイルをFlask経由で取得する必要が無いのであれば、COREの機能を使う必要はありません。
その場合、backend/appserver/py
を次のように書き換えればOKです。
# appserver.py from backend.application import create_app from flask import render_template import requests # ここを追記 app = create_app() @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def catch_all(path): #### 以下を追記 ### if app.debug: return requests.get('http://localhost:8080/{}'.format(path)).text return render_template("index.html") ################# if __name__ == '__main__': app.run()
必要に応じて以下コマンドを実行。
$ pip install requests
これで、開発モード(DEBUG=1
)の時は、http://localhost:5000
にアクセスした時はhttp://localhost:8080
にリダイレクトされるようになり、Vue.js側の画面でちゃんと乱数が取得できるようになります。
Flask側のサーバhttp://localhost:5000
とVue.js側のサーバhttp://localhost:8080
どちらにアクセスしても同様の結果が得られます。
終わりに
これで、バックエンドのAPIエンドポイントはFlaskで実装し、フロントエンドのUIはVue.jsで実装するフルスタックなアプリケーションが構築できました。
Flask側の開発用サーバとVue.jsの開発用サーバどちらも起動しておけば、それぞれ配下のプログラムを編集した際にホットリロードでリアルタイムに反映されるので、とても開発がしやすいです。
FlaskとVue.jsの詳細な使い方は今後もっと勉強していきたいと思います。