在这个教程中,我将向你展示如何将 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。 如果你没有安装它,请运行下边的命令:

  1. $ npm install -g vue-cli

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

  1. $ mkdir flaskvue
  2. $ cd flaskvue
  3. $ 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

下一步:

  1. $ cd frontend
  2. $ npm install
  3. # 安装完成后运行下边命令
  4. $ npm run dev

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

在 frontend/src/components 文件夹中添加 Home.vue 和 About.vue 两个文件。 并添加如下内容到对应的文件中:

  1. // Home.vue文件的内容
  2. <template>
  3. <div>
  4. <p>主页</p>
  5. </div>
  6. </template>

  1. // About.vue文件的内容
  2. <template>
  3. <div>
  4. <p>关于</p>
  5. </div>
  6. </template>

我们将使用它们正确地识别我们当前的位置 (根据地址栏)。现在,我们需要更改 frontend/src/router/index.js 文件来呈现我们的新组件:

  1. import Vue from 'vue'
  2. import Router from 'vue-router'
  3. const routerOptions = [
  4. { path: '/', component: 'Home' },
  5. { path: '/about', component: 'About' }
  6. ]
  7. const routes = routerOptions.map(route => {
  8. return {
  9. ...route,
  10. component: () => import(`@/components/${route.component}.vue`)
  11. }
  12. })
  13. Vue.use(Router)
  14. export default new Router({
  15. routes,
  16. mode: 'history'
  17. })

如果您尝试输入 localhost:8080 和 localhost:8080/about,您应该会看到相应的页面。

为了创建一个包含静态资产的包,我们几乎已经准备好构建一个项目了。在此之前,让我们为它们重新定义输出目录。在前端 frontend/config/index.js 索引。找到下一个设置

  1. index: path.resolve(__dirname, '../dist/index.html'),
  2. assetsRoot: path.resolve(__dirname, '../dist'),

然后把它们变成下面这样

  1. index: path.resolve(__dirname, '../../dist/index.html'),
  2. assetsRoot: path.resolve(__dirname, '../../dist'),

因此,带有 html/css/js 包的 /dist 文件夹将与 /frontend 具有相同的级别。现在您可以运行 $ npm run build 来创建一个包。

Back-end

我将使用 python 3.6 来进行 flask 应用程序开发。在根目录 /flaskvue 下创建一个子目录来放后端代码,并在子目录中初始化一个虚环境:

  1. $ mkdir backend
  2. $ cd backend
  3. $ virtualenv -p python3 venv

执行下面的命令来激活虚环境 (macOs 操作系统):

  1. $ source venv/bin/activate

在 windows 中激活虚环境请参考此文档 docs.

在虚环境中安装 flask:

  1. (venv) pip install Flask

现在我们开始开发 flask 应用程序。在根目录下创建 run.py 文件:

  1. (venv) cd ..
  2. (venv) touch run.py

将下面代码添加到这个文件中:

  1. from flask import Flask, render_template
  2. app = Flask(__name__,
  3. static_folder = "./dist/static",
  4. template_folder = "./dist")
  5. @app.route('/')
  6. def index():
  7. return render_template("index.html")

这段代码与 Flask starter Hello world 代码略有不同。主要的不同之处在于,我们指定了静态和模板文件夹来用前端包指向 /dist 文件夹,在根文件夹中运行 Flask 服务:

  1. (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 中很容易做到 。将现有的路由修改为如下内容:

  1. @app.route('/', defaults={'path': ''})
  2. @app.route('/<path:path>')
  3. def catch_all(path):
  4. 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 中添加一行:

  1. const routerOptions = [
  2. { path: '/', component: 'Home' },
  3. { path: '/about', component: 'About' },
  4. { path: '*', component: 'NotFound' }
  5. ]

这里的 '*' 是 vue-router 中的通配符,用以代表任何除了我们已经定义好的路由之外的其他情况。 接下来我们在 /components 文件夹中创建一个 NotFound.vue 文件,并写几行简单的代码:

  1. // NotFound.vue
  2. <template>
  3. <div>
  4. <p>404 - Not Found</p>
  5. </div>
  6. </template>

现在通过运行 npm run dev 来重新运行前端服务器,并尝试一些不存在的 URL 链接,例如 localhost:8080/gljhewrgoh 。你就可以看到 “Not Found” 的消息提示了.

添加 API 端点

我的 'Vue.js/Flask' 的最后一个例子。 'Vue.js/Flask' 教程将在服务器端创建 API 并在客户端发送。我将创建一个简单的端点,它将返回一个从 1 到 100 的随机数。

打开 run.py 并添加:

  1. from flask import Flask, render_template, jsonify
  2. from random import *
  3. app = Flask(__name__,
  4. static_folder = "./dist/static",
  5. template_folder = "./dist")
  6. @app.route('/api/random')
  7. def random_number():
  8. response = {
  9. 'randomNumber': randint(1, 100)
  10. }
  11. return jsonify(response)
  12. @app.route('/', defaults={'path': ''})
  13. @app.route('/<path:path>')
  14. def catch_all(path):
  15. return render_template("index.html")

首先,我从 'Flask' 库导入了 'random' 库和 'jsonify' 函数。然后我添加了新的路由 ' /api/random ' 来返回 JSON,如下所示:

  1. {
  2. "randomNumber": 36
  3. }

您可以通过导航到 localhost:5000/api/random 来测试此路由。

此时,服务器端工作已经完成。是时候在客户端展示了。我会改 Home.vue 组成来显示我的随机数:

  1. <template>
  2. <div>
  3. <p>Home page</p>
  4. <p>Random number from backend: {{ randomNumber }}</p>
  5. <button @click="getRandom">New random number</button>
  6. </div>
  7. </template>
  8.  
  9. <script>
  10. export default {
  11. data () {
  12. return {
  13. randomNumber: 0
  14. }
  15. },
  16. methods: {
  17. getRandomInt (min, max) {
  18. min = Math.ceil(min)
  19. max = Math.floor(max)
  20. return Math.floor(Math.random() * (max - min + 1)) + min
  21. },
  22. getRandom () {
  23. this.randomNumber = this.getRandomInt(1, 100)
  24. }
  25. },
  26. created () {
  27. this.getRandom()
  28. }
  29. }
  30. </script>

在这个阶段,我只是在客户端模拟随机数生成过程。所以,这个组件是这样工作的:

  • 初始化变量 randomNumber 等于 0
  • 在 methods 部分 ,我们又 getRandomInt(min, max) 方法, 它将返回一个指定范围内的数字, getRandom 函数,将调度之前的函数,并将其值赋给 randomNumber
  • 创建组件方法后,将调用 getRandom 来初始化 randomNumber
  • 触发按钮事件后,我们将调用 getRandom 获取新数字

在前端,现在在首页你应该看到我们的随机数产生。让我们把它连接到后端。
为此,我们将使用 ' axios' 库,它允许我们发出 HTTP 请求并返回带有 JSON 响应的 JavaScriptPromise。让我们安装它:

  1. (venv) cd frontend
  2. (venv) npm install --save axios

再次打开 Home.vue 文件并 在 <script> 区域添加一些更改:

  1. import axios from 'axios'
  2. methods: {
  3. getRandom () {
  4. // this.randomNumber = this.getRandomInt(1, 100)
  5. this.randomNumber = this.getRandomFromBackend()
  6. },
  7. getRandomFromBackend () {
  8. const path = `http://localhost:5000/api/random`
  9. axios.get(path)
  10. .then(response => {
  11. this.randomNumber = response.data.randomNumber
  12. })
  13. .catch(error => {
  14. console.log(error)
  15. })
  16. }
  17. }

在最开始我们导入 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/),让我们安装它

  1. (venv) pip install -U flask-cors

您可以阅读插件的文档,文档中更好地说明了再服务器上启用 CORS 的方法。 我将使用特定于资源的方法并将 {“origin”“:”*“} 应用于所有 / api / * 路由(所以每个人都可以使用我的 / api 端点)。在 run.py中:

  1. from flask_cors import CORS
  2. app = Flask(__name__,
  3. static_folder = "./dist/static",
  4. template_folder = "./dist")
  5. cors = CORS(app, resources={r"/api/*": {"origins": "*"}})

通过以上更改,您可以直接从前端开发服务器调用 Flask API。

更新:

实际上,如果你通过 Flask 提供静态文件,则不需要更新 CORS 扩展。 感谢 [Carson Gee](https://github.com/carsongee)这个技巧

解决思路如下。 如果应用程序处于调试模式,它将只代理我们的前端服务器。 否则(在生产模式)提供静态文件。 以下是实现的代码:

  1. import requests
  2.  
  3. @app.route('/', defaults={'path': ''})
  4. @app.route('/<path:path>')
  5. def catch_all(path):
  6. if app.debug:
  7. return requests.get('http://localhost:8080/{}'.format(path)).text
  8. return render_template("index.html")

实现方式简单而优雅,像魔术一样✨!

现在,您拥有一个使用自己喜欢的技术构建的全栈应用程序啦。

后记

最后,我想就如何改进此解决方案说几句话。

首先,只有在您想要让 API 可供外部服务器访问时才使用 CORS 扩展。 否则只需使用代理前端开发服务器的技巧。

另一项改进是避免在前端硬编码 API 路由。 也许您需要创建一个包含 API 路由名称的词汇集。 因此,当您更改 API 路由时,您只需刷新这个词汇集即可。 前端关于路由名称的代码不需要更改。

通常在开发过程中,您将至少需要两个终端窗口:一个用于 Flask ,另一个用于 Vue.js 。 在生产环境中,你将不需要为 Vue 运行单独的 Node.js 服务器。

获取源码及相关视频教程加群887934385 免费领取

使用 Flask 和 Vue.js 来构建全栈单页应用的更多相关文章

  1. 微信小程序商城构建全栈应用 Thinkphp5

    课程——微信小程序商城构建全栈应用[目录]第1章 前言:不同的时代,不同的Web第2章 环境,工具与准备工作第3章 模块,路由与获取请求参数第4章 构建验证层第5章 REST与RESTFul第6章 A ...

  2. Senna.js – 速度极快的单页应用程序引擎

    Senna.js 是一个速度超快的单页应用程序引擎,提供了几个低级别的 API,可以帮助你打造现代化的基于 Web 的应用程序.更重要的是,搜索引擎蜘蛛应该能够索引相同的内容. 通过使用 HTML5 ...

  3. Vuebnb 一个用 vue.js + Laravel 构建的全栈应用

    今年我一直在写一本新书叫全栈Vue网站开发:Vue.js,Vuex和Laravel.它会在Packt出版社在2018年初出版. 这本书是围绕着一个案例研究项目,Vuebnb,简单克隆Airbnb.在这 ...

  4. 前端面试题总结(js、html、小程序、React、ES6、Vue、算法、全栈热门视频资源)

    写在前面 参考答案及资源在看云平台发布,如果大家想领取资源以及查看答案,可直接前去购买.一次购买永久可看,文档长期更新!有什么意见与建议欢迎您及时联系作者或留言回复! 文档描述 本文是关注微信小程序的 ...

  5. vue.js项目构建

    这里构建的vue.js项目依赖node服务器运行. 项目搭建完整步骤: 安装node.js ,转至nodeJs网站http://nodejs.cn/ 下载nodeJs进行安装. 安装完毕检查nodeJ ...

  6. flask 与 vue.js 2.0 实现 todo list

    实现了后端与前端分离,后端提供 RESTful api. 后端 flask 与前端 vue 的数据传输都是 json. 本文使用 vue.js 2.0 对前一个例子:flask, SQLAlchemy ...

  7. 转载: 我如何使用 Django + Vue.js 快速构建项目

    原文链接: https://www.ctolib.com/topics-109796.html 正文引用如下 引言 大U的技术课堂 的新年第一课,祝大家新的一年好好学习,天天向上:) 本篇将手把手教你 ...

  8. MVC + Vue.js 初体验(实现表单操作)

    Vuejs http://cn.vuejs.org/ Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的 ...

  9. 一个关于vue+mysql+express的全栈项目(一)

    最近学了mysql数据库,寻思着能不能构思一个小的全栈项目,思来想去,于是就有了下面的项目: 先上几张效果图吧       目前暂时前端只有这几个页面,后端开发方面,有登录,注册,完善用户信息,获取用 ...

随机推荐

  1. Spring入门介绍

    概述 下载地址:https://repo.spring.io/release/org/springframework/spring/ spring是开源的轻量级框架 spring核心的主要两部分 AO ...

  2. 星级评分条(RatingBar)的功能与用法

    星级评分条与拖动条有相同的父类:AbsSeekBar,因此它们十分相似.实际上星际评分条与拖动条的用法.功能都十分接近:它们都允许用户通过拖动来改变进度.RatingBar与SeekBar的最大区别在 ...

  3. MIT线性代数:8.求解Ax=b,可解性和结构

  4. 学习笔记55_Nhibernate

    另一种ORM框架 1.添加各种dll 2.添加配置信息,根据文档直接复制粘贴.config //一般下载Nhibernate-3.0.0.Alpha2-bin包,会有Configuration_Tem ...

  5. Python Socket学习之旅(二)

    Socket函数 注解: Socket的close和shutdown--结束数据传输: close-----关闭本进程的socket id,但链接还是开着的,用这个socket id的其它进程还能用这 ...

  6. javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: ResultSet is from UPDATE. No Data.

    Java jpa调用存储过程,抛出异常如下: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCEx ...

  7. vue-create 报错 command failed: yarn --registry=https://registry.npm.taobao.org --disturl=https://npm.taobao.org/dist 完美解决方案

    @vue/cli 3.x 创建项目失败解决方案 报错信息 command failed: yarn --registry=https://registry.npm.taobao.org --distu ...

  8. C++中对C的扩展学习新增语法——作用域运算符::

    作用域运算符用来告诉编译器在哪个作用域范围搜索符号,一般分为以下3种: 全局作用域: 命名空间作用域: 类作用域:

  9. VMware Workstation Pro(15.5)下安装Windows_Server_2008_R2

    一.新建虚拟机 1.打开VMware Workstation Pro 15.5虚拟机,点击新建虚拟机 2.选择典型(推荐),单击下一步 3.选最后一个 稍后安装操作系统,点击下一步 4.进来页面,选择 ...

  10. pat 1092 To Buy or Not to Buy(20 分)

    1092 To Buy or Not to Buy(20 分) Eva would like to make a string of beads with her favorite colors so ...