在這個教程中,我們將講解如何將vue.js單頁應用與Flask後端進行連線。
一般來說,如果你只是想通過Flask模板使用vue.js庫也是沒有問題的。但是,實際上是一個很明顯的問題那就是,Jinja(模板引擎)也和Vue.js一樣採用雙大括號用於渲染,但只是一個還算過的去的解決方案。
我想要一個不同的例子。如果我需要建立一個單頁應用程式(應用程式使用單頁組成,vue-router在HTML5的History-mode以及其他更多好用的功能)用vue.js,由Flask提供Web服務?簡單地說應該這樣,如下所示:
Flask為index.html服務,index.html包含我的vue.js App。
在前端開發中我使用Webpack,它提供了所有很酷的功能。
Flask有API端,我可以從我的SPA訪問。
我可以訪問API端,甚至當我為了前端開發而執行Node.js的時候。
聽起來是不是很有趣?那讓我們這樣動手做做吧。
完整的原始碼,你可以在這裡找到:
https://github.com/oleg-agapov/flask-vue-spa
客戶端
我將使用Vue CLI產生基本vue.js App。如果你還沒有安裝它,請執行:
$ npm install -g vue-cli
複製程式碼
客戶端和後端程式碼將被拆分到不同的資料夾。初始化前端部分執行跟蹤:
$ mkdir flaskvue
$ cd flaskvue
$ vue init webpack frontend
複製程式碼
通過安裝嚮導。我的設定是:
-
Vue 只在執行時構建。
-
安裝Vue-router。
-
使用ESLint檢查程式碼。
-
選擇一個ESLint 標準預設 。
-
不試用Karma + Mocha進行單位測試。
-
不使用Nightwatch建立端到端的測試。
ok,接著來:
$ cd frontend
$ npm install
# after installation
$ npm run dev
複製程式碼
這就可以開始安裝vue.js應用程式。讓我們從新增一些頁面開始吧。
新增home.vue和about.vue到frontend/src/components資料夾。它們非常簡單,像這樣:
// Home.vue
<template>
<div>
<p>Home page</p>
</div>
</template>
複製程式碼
and
// About.vue
<template>
<div>
<p>About</p>
</div>
</template>
複製程式碼
我們將使用它們正確地識別我們當前的位置(根據位址列)。現在我們需要改變frontend/src/router/index.js檔案以便使用我們的新元件:
import Vue from 'vue'
import Router from 'vue-router'
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,你應該看到相應的頁面。
我們幾乎已經準備好構建一個專案,並且能夠建立一個靜態資原始檔包。在此之前,讓我們為它們重新定義一下輸出目錄。在frontend/config/index.js找到下一個設定:
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
複製程式碼
把它們改為
index: path.resolve(__dirname, '../../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../../dist'),
複製程式碼
所以/dist資料夾的HTML、CSS、JS會在同一級目錄/frontend 。現在你可以執行 $ npm run build 建立一個包。
後端
對於Flask伺服器,我將使用Python版本3.6。在 /flaskvue建立新的子資料夾存放後端程式碼並初始化虛擬環境:
$ mkdir backend
$ cd backend
$ virtualenv -p python3 venv
複製程式碼
為了使虛擬環境中執行(MacOS):
$ source venv/bin/activate
複製程式碼
在Windows中需要啟用此文件(http://pymote.readthedocs.io/en/latest/install/windows_virtualenv.html)。
在虛擬環境下安裝:
(venv) pip install Flask
複製程式碼
現在讓我們為Flask服務端編寫程式碼。建立根目錄檔案run.py:
(venv) cd ..
(venv) touch run.py
複製程式碼
向這個檔案新增下一個程式碼:
from flask import Flask, render_template
app = Flask(__name__,
static_folder = "./dist/static",
template_folder = "./dist")
@app.route('/')
def index():
return render_template("index.html")
複製程式碼
這段程式碼與Flask的 **“Hello World”**程式碼略有不同。主要的區別是,我們指定儲存靜態檔案和模板位置在資料夾 /dist,以便和我們的前端資料夾區別開。在根資料夾中執行Flask服務端:
(venv) FLASK_APP=run.py FLASK_DEBUG=1 flask run
複製程式碼
這將啟動本地主機上的Web伺服器:localhost:5000 上的FLASK_APP伺服器端的啟動檔案,flask_debug = 1將執行在除錯模式。如果一切正確,你會看到熟悉的主頁,你已經完成了對Vue的設定。
同時,如果您嘗試輸入/about頁面,您將面臨一個錯誤。Flask丟擲一個錯誤,說找不到請求的URL。事實上,因為我們使用了HTML5的History-Mode在Vue-router需要配置Web伺服器的重定向,將所有路徑指向index.html。用Flask做起來很容易。將現有路由修改為以下:
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
return render_template("index.html")
複製程式碼
現在輸入網址localhost:5000/about 將重新定向到index.html和vue-router將處理路由。
新增404頁
因為我們有一個包羅永珍的路徑,我們的Web伺服器在現在已經很難趕上404錯誤,Flask將所有請求指向index.html(甚至不存在的頁面)。所以我們需要處理未知的路徑在vue.js應用。當然,所有的工作都可以在我們的路由檔案中完成。
在frontend/src/router/index.js新增下一行:
const routerOptions = [
{ path: '/', component: 'Home' },
{ path: '/about', component: 'About' },
{ path: '*', component: 'NotFound' }
]
複製程式碼
這裡的路徑'*'是一個萬用字元, Vue-router就知道除了我們上面定義的所有其他任何路徑。現在我們需要更多的創造NotFound.vue檔案在**/components**目錄。試一下很簡單:
// NotFound.vue
<template>
<div>
<p>404 - Not Found</p>
</div>
</template>
複製程式碼
現在執行的前端伺服器再次npm run dev,嘗試進入一些毫無意義的地址例如:localhost:8080/gljhewrgoh。您應該看到我們的“未找到”訊息。
新增API端
我們的vue.js/flask教程的最後一個例子將是伺服器端API建立和排程客戶端。我們將建立一個簡單的Api,它將從1到100返回一個隨機數。
開啟run.py並新增:
from flask import Flask, render_template, jsonify
from random import *
app = Flask(__name__,
static_folder = "./dist/static",
template_folder = "./dist")
@app.route('/api/random')
def random_number():
response = {
'randomNumber': randint(1, 100)
}
return jsonify(response)
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
return render_template("index.html")
複製程式碼
首先我匯入random庫和jsonify函式從Flask庫中。然後我新增了新的路由 /api/random來返回像這樣的JSON:
{
"randomNumber": 36
}
複製程式碼
你可以通過本地瀏覽測試這個路徑:localhost:5000/api/random。
此時伺服器端工作已經完成。是時候在客戶端顯示了。我們來改變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>
export default {
data () {
return {
randomNumber: 0
}
},
methods: {
getRandomInt (min, max) {
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min + 1)) + min
},
getRandom () {
this.randomNumber = this.getRandomInt(1, 100)
}
},
created () {
this.getRandom()
}
}
</script>
複製程式碼
在這個階段,我們只是模仿客戶端的隨機數生成過程。所以,這個元件就是這樣工作的:
- 在初始化變數 randomNumber等於0。
- 在methods部分我們通過getRandomInt(min, max)功能來從指定的範圍內返回一個隨機數,getrandom函式將生成隨機數並將賦值給randomNumber
- 元件方法getrandom建立後將會被呼叫來初始化隨機數
- 在按鈕的單擊事件我們將用getrandom方法得到新的隨機數
現在在主頁上,你應該看到前端顯示我們產生的隨機數。讓我們把它連線到後端。
為此目的,我將用axios庫。它允許我們用響應HTTP請求並用Json返回JavaScript Promise。我們安裝下它:
(venv) cd frontend
(venv) npm install --save axios
複製程式碼
開啟 home.vue 再在 <script> 部分新增一些變化:
import axios from 'axios'
methods: {
getRandom () {
// this.randomNumber = this.getRandomInt(1, 100)
this.randomNumber = this.getRandomFromBackend()
},
getRandomFromBackend () {
const path = `http://localhost:5000/api/random`
axios.get(path)
.then(response => {
this.randomNumber = response.data.randomNumber
})
.catch(error => {
console.log(error)
})
}
}
複製程式碼
在頂部,我們需要引用Axios庫。然後有一個新的方法getrandomfrombackend將使用Axios非同步呼叫API和檢索結果。最後,getrandom方法現在應該使用getrandomfrombackend函式得到一個隨機值。
儲存檔案,到瀏覽器,執行一個開發伺服器再次重新整理 localhost:8080。你應該看到控制檯錯誤沒有隨機值。但別擔心,一切都正常。我們得到了CORS的錯誤意味著Flask伺服器API預設會關閉其他Web伺服器(在我們這裡,vue.js App是在 Node.js伺服器上執行的應用程式)。如果你npm run build 專案,那在localhost:5000(如Flask伺服器)你會看到App在工作的。但是,每次對客戶端應用程式進行一些更改時,都建立一個包並不十分方便。
讓我們用打包了CORS外掛的Flask,將使我們能夠建立一個API訪問規則。外掛叫做FlaskCORS,讓我們安裝它:
(venv) pip install -U flask-cors
複製程式碼
你可以閱讀文件,更好的解釋你要使你的伺服器怎麼樣使用CORS。我將使用特定的方法,並將**{“origins”: “*”}**應用於所有/api/*路由(這樣每個人都可以使用我的API端)。在run.py加上:
from flask_cors import CORS
app = Flask(__name__,
static_folder = "./dist/static",
template_folder = "./dist")
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
複製程式碼
有了這種改變,您就可以從前端呼叫服務端。
更新:
事實上,如果你想通過Flask提供靜態檔案不需要CORS。感謝Carson Gee的下面的這一招。
這個主意是這樣的。如果應用程式在除錯模式下,它只會代理我們的前端伺服器。否則(在生產中)只為靜態檔案服務。所以我們這樣做:
import requests
@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")
複製程式碼
很優雅的魔法✨!
現在有了一個完整的全棧**(full-stack)應用程式,用您最喜愛Vue.js和Flask**技術構建。
後記
最後,我想就如何改進這個解決方案談幾句話。
首先利用CORS,如果你想讓你的API端訪問外部的伺服器。否則的話只需要使用代理伺服器與前端開發技巧。
另一個改進是避免客戶端硬編碼API路由。也許你需要考慮一些API端點的字典。因此,當您更改API路由時,只需重新整理一個字典即可。前端仍然有一個有效的端點。
通常在開發過程中,你將至少有2個終端視窗:一個是Flask和另一個是vue.js。在生產中可以擺脫Vue而只單獨執行Node.js伺服器。
原始碼:https://github.com/oleg-agapov/flask-vue-spa
謝謝你的閱讀!
匯智網(www.hubwiz.com)小智原創翻譯!!!
分享一個Vue.js 2 的入門級全家桶系列教程:
- vue.js 2 入門與提高: xc.hubwiz.com/course/vue.…
- vuex 2 入門與提高: xc.hubwiz.com/course/vuex
- vue-router 2 入門與提高: xc.hubwiz.com/course/vuer…
- vue.js 2 工程化實踐: xc.hubwiz.com/course/vueg…
另外推薦一個flask的入門教程給大家:
深入淺出 flask http://xc.hubwiz.com/course/562427361bc20c980538e26f