在这个教程中, 我们将讲解如何将 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 文件夹的 HTMLCSSJS 会在同一级目录 / 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")
很优雅的魔法: sparkles:!
现在有了一个完整的全栈 **(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
来源: http://www.jb51.net/article/136806.htm