Flask 和 Vue.js 開發及整合部署實例


本文主要參考 Flask和Vue.js構建全棧單頁面web應用【通過Flask開發RESTful API】的前部分,英文原文在這里 Developing a Single Page App with Flask and Vue.js

開發過程中我們可以保持 Flask 和  Vue.js 為單獨的兩個項目,並啟動各自的服務,比如 Flask 是 http://localhost:5000, Vue.js 項目通過 npm run serve 啟動在 http://localhost:8080,借助於 node js 的功能,修改 Vue.js 項目的內容能夠自動刷新網頁。要是開發中把靜態文件全放在  Flask 項目中,那么任何對靜態文件的修改都必須重啟 Flask  服務。雖然 Debug 模式啟動的 Flask 在看到它的目錄中有任何修改時也能自動重啟,但對靜態文件的修改重啟 Flask 沒這個必要性。 

但部署時需進一步整合,最終只需要啟動 Flask  服務,而無須兩個,方便部署。如果是以 Docker 容器的方式發布,使用 docker-compose 來編排兩個容器來發布也還算不錯。更專業的部署方式應該是 Vue.js 的靜態內容放到專門的 Web 服務器,如  Apache/Nginx 中,Flask 也通過 wsgi 與 Web 服務器集成起來。

介於原文中所用的 Vue CLI 稍稍顯老,所以實踐中也有些區別,先注明本文寫作時所依賴的各主要組件版本

  • Vue v2.6.11
  • Vue CLI v4.6.6
  • Node v14.4.0
  • npm v6.14.4
  • Flask v1.1.2
  • Python v3.7

創建 Flask 項目

創建項目目錄

$ mkdir flask-vue-app
$ cd flask-vue-app

接下來創建 Python 虛擬環境

$ python3.7 -m venv .venv
$ source .venv/bin/activate

安裝 Flask 和 Flask-CORS 擴展,前面說過,由於開發中啟動了兩個服務,需要跨域訪問服務,所以要用到 Flask-CORS

(.venv) $ pip install flask-cors

Flask 本身會被自動安裝,當前日期為 2020-06-30, 所安裝的 flask-cors 版本為 3.0.8, Fask 為 1.1.2。也可以鎖定版本來安裝擴展,如  pip install flask-cors==3.0.8。現在查看下所有的第三方依賴

$ pip freeze
click==7.1.2
Flask==1.1.2
Flask-Cors==3.0.8
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
six==1.15.0
Werkzeug==1.0.1

有需要的話,保存為 requirements.txt 放到版本服務器上

現在在 flask-vue-app 下創建一個  backend 目錄,並在其中創建文件 app.py, 內容為

 

 

簡單說明一下上面的代碼

  1. CORS(app, resources={r'/*': {'origins': '*'}}) 允許來自於 Vue 的跨域訪問請求
  2. 定義以 /api/* 開頭的 Flask 的路由,由 Flask 來處理
  3. / 請求直接發送一個靜態文件 /index.html,由於不會用到 Flask 的模板系統,所以也就無需調用  render_template() 方法去渲染。
  4. 后面會將到在 backend 目錄中會建立一個到 Vue.js 項目打包后的 dist 目錄的符號鏈接 static, 所以其中有 index.html 等
  5. @app.rout('/<path:fallback>') 里是個關鍵,凡是 Flask 未定義的路由都會落到這里來。如果訪問的是 static(dis) 中的 css, js, img 或 favicon.ico 文件,直接送出內容,其他的請求轉到 Vue 的入口 index.html, 最后將由 Vue 中定義的路由來處理
  6. 如果 Vue 的 Router 工作在 hash 模式的話,fallback 方法可以不要,因為 /#/home 到 /#/about 的切換本身不產生 HTTP 請求,Flask 只需要 / 一個路由進入 Vue 入口頁面

運行 Flask

(.venv) $ python backend/app.py

Flask 會在 localhost:5000 中啟動服務,用 curl 命令驗證

$ curl http://localhost:5000/api/ping
"pong!"

創建 Vue 項目

開始轉到 Vue 項目來,將使用 Vue CLI 工具來生成它,首先是安裝  Vue CLI

$ npm install -g @vue/cli

當前日期 2020-06-30, 安裝后用 vue --version 看到的版本是 @vue/cli 4.4.6。安裝時欲鎖定版本用命令 npm install -g @vue/cli@4.4.6

正式創建項目 frontend,在 flask-vue-app 目錄下運行

$ vue create frontend                  # 選擇 Manually select features, 接下回答幾個問題

啟動 Vue 服務

$ cd frontend
$ npm run serve

打開瀏覽器訪問 http://localhost:8080 會有一個  "Wellcome to Your Vue.js App" 的界面。后面對 frontend 項目的修改會自動刷新網頁。

下面是如何在 Vue.js(8080) 中調用到 Flask(5000) 的 /api/ping 服務,當前在 frontend 目錄中

創建 src/components/Ping.vue 文件,內容為

 

 

編輯 src/router/index.js 文件,高亮行為新加的內容

 

 

對 src/App.vue 的 <template> 中的導航部分刪除,內容變為

 

 

瀏覽器中訪問 http://localhost:8080/ping, "Hello!" 顯示的還是 src/components/Ping.vue 中 data 的內容

現在開始將  Ping.vue 與 Flask 的 /api/ping API 進行連接,Vue 中要用 Ajax 來訪問,先要安裝 axios,命令如下

$ npm install axios --save

 

目前安裝的是 axios@0.19.2, 安裝后可在 package.json 里看到 dependencies 中的 "axios": "^0.19.2"

編輯 src/components/Ping.vue 文件,修改為

 

 

高亮行為新加的代碼, 保存后 http://localhost:8080/ping 窗口中的內容自動刷新為

pong! 消息是來自於 Flask 的 /api/ping API 的響應。由於我們前面是以 Debug 模式啟動的 Flask backend 應用, 所以在控制台也能夠看到一個對 /api/ping 的請求

127.0.0.1 - - [01/Jul/2020 02:53:06] "GET /api/ping HTTP/1.1" 200 -

訪問 http://localhost:8080/ping_xyz 指向了同一個 Vue 組件,所以效果上與 http://localhost:8080/ping 是一樣的。

Flask 與 Vue.js  整合

開發的時候啟動兩個服務很方面,但我們希望在部署后只啟動一個 Flask  服務,那么可以這樣做

首先用 npm 對 fronend 中的靜態內容打包

$ npm run build

將會在 frontend 下生成 dist 目錄,其下內容為

css      favicon.ico   img  index.html   js

綠色為目錄

這時修只要在 backend 中創建一個符號鏈接

$ ln -s ../front/dist static

創建后在 backend 目錄中的內容為

-rw-r--r-- 1  yanbin root 690 Jul 1 01:01 app.py
lrwxr-xr-x 1 yanbin root 16 Jun 30 22:33 static -> ../frontend/dist

因為 Flask 是以 Debug 模式啟動的,對 Flask 項目 backend 的改動也可能會觸發  Flask 的重新啟動,需要的話手動重啟  Flask (CTRL+C 退出再重啟)

$ python backend/app.py

現在  Vue.js 那個服務可以停止了,不管是 Flask 還是 Vue.js  的路由都能夠通過  http://localhost:5000 來訪問了

http://localhost:5000/ping

 

http://localhost:5000/ping_xyz

Flask + Vue 對 http://localhost:5000/ping 和  http://localhost:5000/ping_xyz 的處理過程是

  1. 對 localhost:5000 的請求發往 Flask, Flask 的  @app.route('/<path:fallback>') 進行處理
  2. 不是 css/js/img 和  favicon.iso 的請求,交由 Vue.js 的入口 index.html 處理
  3. Vue.js 在自己的路由表中找到了 /ping 和 /ping_xyz, 進它們進行渲染
  4. 如隨意一個 http://localhost:5000/abc,也會轉給 Vue.js 的入口 index.html,但 Vue.js 未定 /abc 路由,頁面得不到渲染,一面空白

最后,Flask 與 Vue.js 這樣整合后,Vue.js 路由中訪問 Flask API 要與 Flask 實際啟動的 IP 端口保持一致,因為只有一個服務也就不存在跨域訪問的問題,允許跨域相關的 Python 代碼也就可以移除掉了。

本文演示的是一個 Vue.js 多頁面程序,如果是單頁面程序(用 /#/abc) 導引的,在 Flask 中處理起來還稍微簡單些,只要 "/" 請求交給 Vue.js 的入口 index.html, 其他全當是靜態文件,Flask 的 API 還是最好約定為 /api/* 的形式。

VueRouter 的 history 和 hash 模式

如果 VueRouter  使用 hash 模式,在服務端可以更簡單的些,前面說過在 app.py 中的 fallback() 方法可以不需要了。Vue 默認的模式是 hash, 只是用 vue 命令生成的項目設置成了 history 模式,重新啟用 hash 模式的方法是修改 src/router/index.js 文件中,把 mode 值改為 hash 或去掉 mode 行

 

 

這時候打開 http://localhost:8080 會自動跳轉到 http://localhost:8080/#/, 其他的路由也加上了 #, 如 /#/ping 

瀏覽時看到原來的 localhost:8080/ping 變成了 localhost:8080/#/ping, 使用 hash 的好處是每次 Vue 的路由跳轉其時是一個錨點鏈接(anchor),它相當於當前頁的位置跳轉,不會重新刷新整個頁面,且本身不會產生與服務端的 HTTP 請求,所以可減少許多的因 Vue  跳轉而產生的交互,雖然前也簡單的跳轉回 Vue 的入口文件 index.html,但怎么着也是省了不少來回。

接下來將在 Vue.js 中試驗 Bootstrap 和 BootstrapVue 的集成。

本實例代碼已推送到了 github, 倉庫地址為 https://github.com/yabqiu/flask-vue-app.git,姓沒變,歡迎檢閱

相關鏈接:

    1. Flask和Vue.js構建全棧單頁面web應用【通過Flask開發RESTful API】
    2. Developing a Single Page App with Flask and Vue.js
    3. Vue SPA and Flask together
    4. Best practices to deploy a Flask and Vue app?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM