使用 Flask 和 Vue.js 来构建全栈单页应用

在这个教程中,我将向你展现如何将 Vue 的单页面应用和 Flask 后端链接起来。css

简单的来讲,若是想在 Flask 中使用 Vue 框架是没有什么问题的。 但在实际中存在一个明显的问题就是 Flask 的模版引擎 Jija 和 Vue 同样使用双花括号来渲染,对于 Jinja 模板和 Vue 的语法冲突问题,这里有一个很好的解决方案  herehtml

我想作个不同的。 作一个用 Vue.js 作前端(用单页组件,HTML5 历史模式的「vue-router」,以及其余好的特性),用 Flask 作后端的单页应用怎么样? 简单地说,这个应用应该是这样的:前端

  • Flask 用来驱动一个包含 Vue.js app 的「index.html」,
  • 前端开发过程当中我用到 Webpack 和它提供的全部酷的特性
  • Flask 有我能从 SPA 访问到的 API 端口
  • 在我开发前端时,我能运行 Node.js 来访问 api 端口

听起来颇有意思吧?咱们开始吧。vue

如下是全部代码的连接
https://github.com/oleg-agapov/flask-vue-s...node

客户端

为了生成基本的 Vue.js 文件结构,我将使用 vue-cli。 若是你没有安装它,请运行下边的命令:python

$ npm install -g vue-cli

客户端和后端代码将会被拆分到不一样的文件夹中, 请运行下边命令初始化前端部分:webpack

$ mkdir flaskvue $ cd flaskvue $ vue init webpack frontend

下边是安装过程当中个人设置:ios

  • 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

下一步:git

$ cd frontend $ npm install # 安装完成后运行下边命令
$ npm run dev

到这里,你应该安装好 Vue.js 了吧!那就让咱们添加一些页面。github

 在 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 免费领取

相关文章
相关标签/搜索