使用 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 端口
听起来很有意思吧?我们开始吧。
客户端
为了生成基本的 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 免费领取
使用 Flask 和 Vue.js 来构建全栈单页应用的更多相关文章
- 微信小程序商城构建全栈应用 Thinkphp5
课程——微信小程序商城构建全栈应用[目录]第1章 前言:不同的时代,不同的Web第2章 环境,工具与准备工作第3章 模块,路由与获取请求参数第4章 构建验证层第5章 REST与RESTFul第6章 A ...
- Senna.js – 速度极快的单页应用程序引擎
Senna.js 是一个速度超快的单页应用程序引擎,提供了几个低级别的 API,可以帮助你打造现代化的基于 Web 的应用程序.更重要的是,搜索引擎蜘蛛应该能够索引相同的内容. 通过使用 HTML5 ...
- Vuebnb 一个用 vue.js + Laravel 构建的全栈应用
今年我一直在写一本新书叫全栈Vue网站开发:Vue.js,Vuex和Laravel.它会在Packt出版社在2018年初出版. 这本书是围绕着一个案例研究项目,Vuebnb,简单克隆Airbnb.在这 ...
- 前端面试题总结(js、html、小程序、React、ES6、Vue、算法、全栈热门视频资源)
写在前面 参考答案及资源在看云平台发布,如果大家想领取资源以及查看答案,可直接前去购买.一次购买永久可看,文档长期更新!有什么意见与建议欢迎您及时联系作者或留言回复! 文档描述 本文是关注微信小程序的 ...
- vue.js项目构建
这里构建的vue.js项目依赖node服务器运行. 项目搭建完整步骤: 安装node.js ,转至nodeJs网站http://nodejs.cn/ 下载nodeJs进行安装. 安装完毕检查nodeJ ...
- flask 与 vue.js 2.0 实现 todo list
实现了后端与前端分离,后端提供 RESTful api. 后端 flask 与前端 vue 的数据传输都是 json. 本文使用 vue.js 2.0 对前一个例子:flask, SQLAlchemy ...
- 转载: 我如何使用 Django + Vue.js 快速构建项目
原文链接: https://www.ctolib.com/topics-109796.html 正文引用如下 引言 大U的技术课堂 的新年第一课,祝大家新的一年好好学习,天天向上:) 本篇将手把手教你 ...
- MVC + Vue.js 初体验(实现表单操作)
Vuejs http://cn.vuejs.org/ Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的 ...
- 一个关于vue+mysql+express的全栈项目(一)
最近学了mysql数据库,寻思着能不能构思一个小的全栈项目,思来想去,于是就有了下面的项目: 先上几张效果图吧 目前暂时前端只有这几个页面,后端开发方面,有登录,注册,完善用户信息,获取用 ...
随机推荐
- Spring入门介绍
概述 下载地址:https://repo.spring.io/release/org/springframework/spring/ spring是开源的轻量级框架 spring核心的主要两部分 AO ...
- 星级评分条(RatingBar)的功能与用法
星级评分条与拖动条有相同的父类:AbsSeekBar,因此它们十分相似.实际上星际评分条与拖动条的用法.功能都十分接近:它们都允许用户通过拖动来改变进度.RatingBar与SeekBar的最大区别在 ...
- MIT线性代数:8.求解Ax=b,可解性和结构
- 学习笔记55_Nhibernate
另一种ORM框架 1.添加各种dll 2.添加配置信息,根据文档直接复制粘贴.config //一般下载Nhibernate-3.0.0.Alpha2-bin包,会有Configuration_Tem ...
- Python Socket学习之旅(二)
Socket函数 注解: Socket的close和shutdown--结束数据传输: close-----关闭本进程的socket id,但链接还是开着的,用这个socket id的其它进程还能用这 ...
- javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: ResultSet is from UPDATE. No Data.
Java jpa调用存储过程,抛出异常如下: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCEx ...
- 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 ...
- C++中对C的扩展学习新增语法——作用域运算符::
作用域运算符用来告诉编译器在哪个作用域范围搜索符号,一般分为以下3种: 全局作用域: 命名空间作用域: 类作用域:
- VMware Workstation Pro(15.5)下安装Windows_Server_2008_R2
一.新建虚拟机 1.打开VMware Workstation Pro 15.5虚拟机,点击新建虚拟机 2.选择典型(推荐),单击下一步 3.选最后一个 稍后安装操作系统,点击下一步 4.进来页面,选择 ...
- 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 ...