使用 Flask 和 Vue.js 來構建全棧單頁應用


在這個教程中,我將向你展示如何將 Vue 的單頁面應用和 Flask 后端連接起來。

簡單的來說,如果想在 Flask 中使用 Vue 框架是沒有什么問題的。 但在實際中存在一個明顯的問題就是 Flask 的模版引擎 Jija 和 Vue 一樣使用雙花括號來渲染,對於 Jinja 模板和 Vue 的語法沖突問題,這里有一個很好的解決方案  here

我想做個不一樣的。 做一個用 Vue.js 做前端(用單頁組件,HTML5 歷史模式的「vue-router」,以及其他好的特性),用 Flask 做后端的單頁應用怎么樣? 簡單地說,這個應用應該是這樣的:

  • Flask 用來驅動一個包含 Vue.js app 的「index.html」,
  • 前端開發過程中我用到 Webpack 和它提供的所有酷的特性
  • Flask 有我能從 SPA 訪問到的 API 端口
  • 在我開發前端時,我能運行 Node.js 來訪問 api 端口

聽起來很有意思吧?我們開始吧。

以下是所有代碼的鏈接
https://github.com/oleg-agapov/flask-vue-s...

客戶端

為了生成基本的 Vue.js 文件結構,我將使用 vue-cli。 如果你沒有安裝它,請運行下邊的命令:

$ npm install -g vue-cli

客戶端和后端代碼將會被拆分到不同的文件夾中, 請運行下邊命令初始化前端部分:

$ mkdir flaskvue
$ cd flaskvue
$ vue init webpack frontend

下邊是安裝過程中我的設置:

  • Vue build --- Runtime only
  • Install vue-router? --- Yes
  • Use ESLint to lint your code? --- Yes
  • Pick an ESLint preset --- Standard
  • Setup unit tests with Karma + Mocha? --- No
  • Setup e2e tests with Nightwatch? --- No

下一步:

$ cd frontend
$ npm install
# 安裝完成后運行下邊命令
$ npm run dev

到這里,你應該安裝好 Vue.js 了吧!那就讓我們添加一些頁面。

 在 frontend/src/components 文件夾中添加 Home.vue 和 About.vue 兩個文件。 並添加如下內容到對應的文件中:

// Home.vue文件的內容
<template>
  <div>
    <p>主頁</p>
  </div>
</template>

// About.vue文件的內容
<template>
  <div>
    <p>關於</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'),

因此,帶有 html/css/js 包的 /dist 文件夾將與 /frontend 具有相同的級別。現在您可以運行 $ npm run build 來創建一個包。

Back-end

我將使用 python 3.6 來進行 flask 應用程序開發。在根目錄 /flaskvue 下創建一個子目錄來放后端代碼,並在子目錄中初始化一個虛環境: 

$ mkdir backend
$ cd backend
$ virtualenv -p python3 venv

執行下面的命令來激活虛環境 (macOs 操作系統):

$ source venv/bin/activate

在 windows 中激活虛環境請參考此文檔 docs.

在虛環境中安裝 flask:

(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 starter Hello world 代碼略有不同。主要的不同之處在於,我們指定了靜態和模板文件夾來用前端包指向 /dist 文件夾,在根文件夾中運行 Flask 服務:

(venv) FLASK_APP=run.py FLASK_DEBUG=1 flask run

這將在 localhost:5000 上啟動一個 web 服務器。FLASK_APP 指向服務器啟動文件,FLASK_DEBUG=1 將在調試模式下運行。如果一切都是正確的,您將看到熟悉的主頁,您在 Vue 上所做的。

與此同時,如果你試圖添加一個 /about 頁面。 Flask 將拋出一個頁面未找到的錯誤。 確實如此,因為我們在 vue-router 中使用了 HTML5 歷史模式,我們需要去 配置我們的服務器 讓所有路由跳轉到 index.html. 這個在 Flask 中很容易做到 。將現有的路由修改為如下內容:

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

新的 URL 鏈接 localhost:5000/about 將會跳轉到 index.html ,並且 vue-router 將會自己處理其余的事情。

 

添加 404 頁面

因為我們定義了一個將所有請求跳轉到 index.html 的路由,因此 Flask 將無法捕獲到 404 錯誤(以及不存在的頁面),將一些找不到頁面的請求也跳轉到 index.html。所以我們需要在 Vue.js 的路由文件中設置一條路由規則去處理這種情況。

在 frontend/src/router/index.js 中添加一行:

const routerOptions = [
  { path: '/', component: 'Home' },
  { path: '/about', component: 'About' },
  { path: '*', component: 'NotFound' }
]

這里的 '*' 是 vue-router 中的通配符,用以代表任何除了我們已經定義好的路由之外的其他情況。 接下來我們在 /components 文件夾中創建一個 NotFound.vue 文件,並寫幾行簡單的代碼:

// NotFound.vue
<template>
  <div>
    <p>404 - Not Found</p>
  </div>
</template>

現在通過運行 npm run dev 來重新運行前端服務器,並嘗試一些不存在的 URL 鏈接,例如 localhost:8080/gljhewrgoh 。你就可以看到 “Not Found” 的消息提示了.

添加 API 端點

我的 'Vue.js/Flask' 的最后一個例子。 'Vue.js/Flask' 教程將在服務器端創建 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")

首先,我從 'Flask' 庫導入了 'random' 庫和 'jsonify' 函數。然后我添加了新的路由 ' /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 來初始化 randomNumber
  • 觸發按鈕事件后,我們將調用 getRandom 獲取新數字

在前端,現在在首頁你應該看到我們的隨機數產生。讓我們把它連接到后端。
為此,我們將使用 ' axios' 庫,它允許我們發出 HTTP 請求並返回帶有 JSON 響應的 JavaScriptPromise。讓我們安裝它:

 

(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](http s://developer.mozilla.org/en-us/docs/web/http/cors)錯誤,這意味着我們的 flask 服務器 API 默認關閉到其他 Web 服務器(在我們的情況下,它是運行 vue.js 應用程序的 node.js 服務器)。如果您使用 npm run build 創建一個 bundle 並打開 localhost:5000(就是 flask 服務器),您將看到正在工作的應用程序。但是,每次對客戶端應用程序進行一些更改時,創建一個包並不十分方便。

讓我們使用 Flask 的 CORS 插件,這將允許我們為 API 訪問創建規則。 插件名為 [flask-cors](http://flask-cors.readthedocs.io/en/latest/),讓我們安裝它

 

(venv) pip install -U flask-cors

 

您可以閱讀插件的文檔,文檔中更好地說明了再服務器上啟用 CORS 的方法。 我將使用特定於資源的方法並將 {“origin”“:”*“} 應用於所有 / 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 API。

更新:

實際上,如果你通過 Flask 提供靜態文件,則不需要更新 CORS 擴展。 感謝 [Carson Gee](https://github.com/carsongee)這個技巧

解決思路如下。 如果應用程序處於調試模式,它將只代理我們的前端服務器。 否則(在生產模式)提供靜態文件。 以下是實現的代碼:

 

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

 

實現方式簡單而優雅,像魔術一樣✨!

現在,您擁有一個使用自己喜歡的技術構建的全棧應用程序啦。

后記

最后,我想就如何改進此解決方案說幾句話。

首先,只有在您想要讓 API 可供外部服務器訪問時才使用 CORS 擴展。 否則只需使用代理前端開發服務器的技巧。

另一項改進是避免在前端硬編碼 API 路由。 也許您需要創建一個包含 API 路由名稱的詞匯集。 因此,當您更改 API 路由時,您只需刷新這個詞匯集即可。 前端關於路由名稱的代碼不需要更改。

通常在開發過程中,您將至少需要兩個終端窗口:一個用於 Flask ,另一個用於 Vue.js 。 在生產環境中,你將不需要為 Vue 運行單獨的 Node.js 服務器。

獲取源碼及相關視頻教程加群887934385 免費領取


免責聲明!

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



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