使用 Vue.js 和Flask实现全栈单页面应用(附完整源码)

论坛 期权论坛 期权     
前端速报   2019-6-10 04:01   2125   0
译者:枫林   链接:http://www.zcfy.cc/article/4491
https://codeburst.io/full-stack-single-page-application-with-vue-js-and-flask-b1e036315532
在本教程中,我将向大家展示如何使用前端的 Vue.js 单页面应用和后端的 Flask 进行交互。
如果你只是想使用 Vue.js 库和 Flask 模板基本上是没什么问题的。但...好吧,其实还是有一个比较显而易见的问题:跟 Vue.js 一样,Jinji(模板引擎)也是使用双大括号来渲染页面,但已经有一个很好的解决方案 在这里 了。
我想要一个跟上面方案有点不同的例子。如果我要一个用 Vue.js(使用单页面组件,在
  1. vue-router
复制代码
开启 HTML5 history 模式,还有使用其他一些非常棒的特性)框架的单页面和 Flask 做后台服务的应用?应该能按下面的要求工作:
  • Flask运行的服务可以访问
    1. index.html
    复制代码
    首页和 Vue.js 应用
  • 在前端开发环境,使用 Webpack 和它提供的很多非常棒的功能
  • 可以从前端的单页面应用访问 Flask 的 API 接口
  • 以 Node.js 服务运行的前端开发环境同样也可以访问 API 接口
这看起来很有趣,不是吗?那就让我们开始吧。
你可以在github上查看所有的源代码:
https://github.com/oleg-agapov/flask-vue-spa
[h2]客户端[/h2]我用 vue-cli 命令行工具搭建起 Vue.js 的基础框架。如果你还没有安装,可以运行:
    1. $ npm install -g vue-cli
    复制代码
客户端和后端代码将会放到不同的文件夹下,初始化前端部分执行如下操作:
    1. $ mkdir flaskvue
    复制代码
    1. $ cd flaskvue
    复制代码
    1. $ vue init webpack frontend
    复制代码
以下是我通过安装向导的项目设置:
  • Vue build—Runtime only (Vue 构建的版本 - 运行时)
  • Install vue-router?—Yes (安装 vue-router?- 是)
  • Use ESLint to lint your code?—Yes (使用 ESLint 校验你的代码?- 是)
  • Pick an ESLint preset—Standard (选择 ESList 的预置版本 - 标准)
  • Setup unit tests with Karma + Mocha?—No (使用 Karma + Mocha 设置单元测试?- 否)
  • Setup e2e tests with Nightwatch?—No (使用 Nightwatch 设置端到端测试?- 否)
下一步:
    1. $ cd frontend
    复制代码
    1. $ npm install
    复制代码
    1. # after installation
    复制代码
    1. $ npm run dev
    复制代码
现在你可以开始设置 Vue.js 应用了。让我们先来添加些页面吧。
添加
  1. Home.vue
复制代码
  1. About.vue
复制代码
  1. frontend/src/components
复制代码
文件夹。像如下简单添加些内容:
    1. // Home.vue
    复制代码
    1. [/code]
    2. [*][code]
    复制代码
    1.   
    复制代码
    1.     Home page
    复制代码
    1.   
    复制代码
    1. [/code]
    2. [/list]和
    3. [list=1][*][code]// About.vue
    复制代码
    1. [/code]
    2. [*][code]
    复制代码
    1.   
    复制代码
    1.     About
    复制代码
    1.   
    复制代码
    1. [/code]
    2. [/list]我们将在本地验证它们(通过地址栏访问)。现在我们要改变 [code]frontend/src/router/index.js
    复制代码
    文件去一个个渲染我们的新组件:
      1. import Vue from 'vue'
      复制代码
      1. import Router from 'vue-router'
      复制代码
      1. [/code]
      2. [*][code]const routerOptions = [
      复制代码
      1.   { path: '/', component: 'Home' },
      复制代码
      1.   { path: '/about', component: 'About' }
      复制代码
      1. ]
      复制代码
      1. [/code]
      2. [*][code]const routes = routerOptions.map(route => {
      复制代码
      1.   return {
      复制代码
      1.     ...route,
      复制代码
      1.     component: () => import(`@/components/${route.component}.vue`)
      复制代码
      1.   }
      复制代码
      1. })
      复制代码
      1. [/code]
      2. [*][code]Vue.use(Router)
      复制代码
      1. [/code]
      2. [*][code]export default new Router({
      复制代码
      1.   routes,
      复制代码
      1.   mode: 'history'
      复制代码
      1. })
      复制代码
    现在如果输入
    1. localhost:8080
    复制代码
    1. localhost:8080/about
    复制代码
    你应该看到相应的页面。


    在我们构建生成项目静态资源前还需要修改它们的输出路径。在
    1. frontend/config/index.js
    复制代码
    找到下面的两行
      1. index: path.resolve(__dirname, '../dist/index.html'),
      复制代码
      1. assetsRoot: path.resolve(__dirname, '../dist'),
      复制代码
    然后成改如下内容
      1. index: path.resolve(__dirname, '../../dist/index.html'),
      复制代码
      1. assetsRoot: path.resolve(__dirname, '../../dist'),
      复制代码
    所以, 包含 html/css/js 静态资源包的
    1. /dist
    复制代码
    文件夹和
    1. /frontend
    复制代码
    在同一级目录下。现在你可以运行
    1. $ npm run build
    复制代码
    去构建项目了


    [h2]后端[/h2]Flask 后端,我将使用 3.6 版本的 python。在根目录
    1. /flaskvue
    复制代码
    文件夹下为后端代码和初始化虚拟环境创建新的子目录:
      1. $ mkdir backend
      复制代码
      1. $ cd backend
      复制代码
      1. $ virtualenv -p python3 venv
      复制代码
    开启虚拟环境执行(mac系统):
      1. $ source venv/bin/activate
      复制代码
    在 Windows 上开启请看这里 docs。
    在虚拟环境中安装 Flask 如下:
      1. (venv) pip install Flask
      复制代码
    现在让我们开始写 Flask 服务器端代码。在根目录下创建
    1. run.py
    复制代码
    文件:
      1. (venv) cd ..
      复制代码
      1. (venv) touch run.py
      复制代码
    然后添加以下代码到这个文件:
      1. from flask import Flask, render_template
      复制代码
      1. [/code]
      2. [*][code]app = Flask(__name__,
      复制代码
      1.             static_folder = "./dist/static",
      复制代码
      1.             template_folder = "./dist")
      复制代码
      1. [/code]
      2. [*][code]@app.route('/')
      复制代码
      1. def index():
      复制代码
      1.     return render_template("index.html")
      复制代码
    上面的代码和 Flask 入门教程 “Hello world” 上的代码稍有不同。最主要的不同点在于我们详细指明了前端的静态和模板文件夹输出到
    1. /dist
    复制代码
    文件夹。然后在根目录下运行 Flask 服务。
      1. (venv) FLASK_APP=run.py FLASK_DEBUG=1 flask run
      复制代码
    这将会在
    1. localhost:5000
    复制代码
    开启一个后台服务。
    1. FLASK_APP
    复制代码
    指向服务启动文件,
    1. FLASK_DEBUG=1
    复制代码
    将会以调试模式运行。如果没有错误,你将会看到熟悉的首页,这样,服务器就成功运行 Vue 应用了。
    与此同时如果你试图访问
    1. /about
    复制代码
    页面将会出现一个错误。Flask 会抛出一个找不到请求地址的错误。实际上是因为在
    1. vue-router
    复制代码
    用了 HTML5 的 history 模式, 所以我们需要配置我们的后台服务去重定向所有的路由都跳转到
    1. index.html
    复制代码
    上。这在 Flask 上可以很简单做到。做如下修改:
      1. @app.route('/', defaults={'path': ''})
      复制代码
      1. @app.route('/')
      复制代码
      1. def catch_all(path):
      复制代码
      1.     return render_template("index.html")
      复制代码
    现在地址
    1. localhost:5000/about
    复制代码
    将会重定向到
    1. index.html
    复制代码
    1. vue-router
    复制代码
    将会在它自己内部处理。
    [h2]添加 404 页面[/h2]因为在我们的后台服务里设置捕捉所有路由是非常困难的,所以我们用 Flask 捕捉 404 错误会重定向 所有 请求到
    1. index.html
    复制代码
    (连同不存在的页面)。在 Vue.js 应用里处理未定义的路由。当然,所有的工作均可在我们的路由文件设置。
    1. frontend/src/router/index.js
    复制代码
    增加一行:
      1. const routerOptions = [
      复制代码
      1.   { path: '/', component: 'Home' },
      复制代码
      1.   { path: '/about', component: 'About' },
      复制代码
      1.   { path: '*', component: 'NotFound' }
      复制代码
      1. ]
      复制代码
    通配符
    1. '*'
    复制代码
    1. vue-router
    复制代码
    里的含义是以上路由定义之外的情况。现在我们需要在
    1. /components
    复制代码
    文件夹新建
    1. NotFound.vue
    复制代码
    文件。我简单地创建它:
      1. // NotFound.vue
      复制代码
      1. [/code]
      2. [*][code]
      复制代码
      1.   
      复制代码
      1.     404 - Not Found
      复制代码
      1.   
      复制代码
      1. [/code]
      2. [/list]现在 通过 [code]npm run dev
      复制代码
      重新启动前台服务然后随意输入网址像
      1. localhost:8080/gljhewrgoh
      复制代码
      。你应该看到 “Not Found” 两个单词。
      [h2]添加后端 API 接口[/h2]我的 Vue.js/Flask 教程的最后一个例子将在后端创建一个 API 接口然后通过前端来调用它。我将创建一个随机返回数字1到100的简单端口。
      打开
      1. run.py
      复制代码
      新增如下代码:
        1. from flask import Flask, render_template, jsonify
        复制代码
        1. from random import *
        复制代码
        1. [/code]
        2. [*][code]app = Flask(__name__,
        复制代码
        1.             static_folder = "./dist/static",
        复制代码
        1.             template_folder = "./dist")
        复制代码
        1. [/code]
        2. [*][code]@app.route('/api/random')
        复制代码
        1. def random_number():
        复制代码
        1.     response = {
        复制代码
        1.         'randomNumber': randint(1, 100)
        复制代码
        1.     }
        复制代码
        1.     return jsonify(response)
        复制代码
        1. [/code]
        2. [*][code]@app.route('/', defaults={'path': ''})
        复制代码
        1. @app.route('/')
        复制代码
        1. def catch_all(path):
        复制代码
        1.     return render_template("index.html")
        复制代码
      我首先从 Flask 资源库导入
      1. random
      复制代码
      库和
      1. jsonify
      复制代码
      函数。然后我增加一个返回 JSON 数据格式的新路由
      1. /api/random
      复制代码
      , 如下:
        1. {
        复制代码
        1.   "randomNumber": 36
        复制代码
        1. }
        复制代码
      你可以通过地址:
      1. localhost:5000/api/random
      复制代码
      来测试这个路由。
      到这里,服务端的工作已经完成了。该到客户端上场了。我将修改
      1. Home.vue
      复制代码
      组件来显示我的随机数字:
        1. [/code]
        2. [*][code]  
        复制代码
        1.     Home page
        复制代码
        1.     Random number from backend: {{ randomNumber }}
        复制代码
        1.     New random number
        复制代码
        1.   
        复制代码
        1. [/code]
        2. [*][code]
        复制代码
        1. [/code]
        2. [*][code]export default {
        复制代码
        1.   data () {
        复制代码
        1.     return {
        复制代码
        1.       randomNumber: 0
        复制代码
        1.     }
        复制代码
        1.   },
        复制代码
        1.   methods: {
        复制代码
        1.     getRandomInt (min, max) {
        复制代码
        1.       min = Math.ceil(min)
        复制代码
        1.       max = Math.floor(max)
        复制代码
        1.       return Math.floor(Math.random() * (max - min + 1)) + min
        复制代码
        1.     },
        复制代码
        1.     getRandom () {
        复制代码
        1.       this.randomNumber = this.getRandomInt(1, 100)
        复制代码
        1.     }
        复制代码
        1.   },
        复制代码
        1.   created () {
        复制代码
        1.     this.getRandom()
        复制代码
        1.   }
        复制代码
        1. }
        复制代码
        1. [/code]
        2. [/list]在这一步,我将在客户端模拟随机数的生成。所以,组件的工作过程如下:
        3. [list][*]初始变量 [code]randomNumber
        复制代码
        等于
        1. 0
        复制代码
        1. methods
        复制代码
        部分,我们用
        1. getRandomInt(min, max)
        复制代码
        函数从指定区间返回一个数字,
        1. getRandom
        复制代码
        函数将调用上一个函数生成一个值赋给
        1. randomNumber
        复制代码
      • 之后在组件被创建时调用
        1. getRandom
        复制代码
        方法给
        1. randomNumber
        复制代码
        赋个初始数值
      • 在按钮点击事件里,我们将触发
        1. getRandom
        复制代码
        方法去得到一个数值
      现在,在首页上你将看到由前端生成的随机数。让我们继续来连接后端。
      我将用 axios 库来连接后端。它将允许我们创建能返回
      1. Promise
      复制代码
      对象的 HTTP 请求。我们先安装它:
        1. (venv) cd frontend
        复制代码
        1. (venv) npm install --save axios
        复制代码
      再次打开
      1. Home.uve
      复制代码
      ,修改
      1. [/code] 部分代码:
      2. [list=1][*][code]import axios from 'axios'
      复制代码
      1. [/code]
      2. [*][code]methods: {
      复制代码
      1.   getRandom () {
      复制代码
      1.     // this.randomNumber = this.getRandomInt(1, 100)
      复制代码
      1.     this.randomNumber = this.getRandomFromBackend()
      复制代码
      1.   },
      复制代码
      1.   getRandomFromBackend () {
      复制代码
      1.     const path = `[http://localhost:5000/api/random`](http://localhost:5000/api/random`)
      复制代码
      1.     axios.get(path)
      复制代码
      1.     .then(response => {
      复制代码
      1.       this.randomNumber = response.data.randomNumber
      复制代码
      1.     })
      复制代码
      1.     .catch(error => {
      复制代码
      1.       console.log(error)
      复制代码
      1.     })
      复制代码
      1.   }
      复制代码
      1. }
      复制代码
    在文件顶部,我们先导入 axios 库。然后用 axios 去异步调用新方法
    1. getRandonFromBackend
    复制代码
    接收返回的结果。最后,
    1. getRandom
    复制代码
    方法调用
    1. getRandomFromBackend
    复制代码
    去获取随机值。
    保存文件,打开浏览器,再次运行前端开发服务器环境,刷新
    1. localhost:8080
    复制代码
    然后... 你应该看到控制台报了没有随机值的错误。但不用担心,一切正常运行中。我们得到 cors 的错误,它的意思是我们的 Flask 后台 API 默认不对其他的域名和端口(我们的例子运行的是 Vue.js 应用)开放。当你用
    1. npm run build
    复制代码
    生成包然后打开
    1. localhost:5000
    复制代码
    (Flask 服务)你会看到应用正常运行不再报错了。但如果每次在客户端改了一点东西都要重新构建包,显然不是很方便。
    Flask 的 CORS 插件允许我们为访问 API 创建规则。插件叫 flask-cors,我们先来安装它:
      1. (venv) pip install -U flask-cors
      复制代码
    你可以通过阅读文档选择更好的方法来在你的服务器上开启 CORS。我这里将会用资源指定的方法应用
    1. {"origins": "*"}
    复制代码
    去允许所有
    1. /api/*
    复制代码
    下的路由(所以任何人都可以访问
    1. /api
    复制代码
    接口)。修改
    1. run.py
    复制代码

      1. from flask_cors import CORS
      复制代码
      1. [/code]
      2. [*][code]app = Flask(__name__,
      复制代码
      1.             static_folder = "./dist/static",
      复制代码
      1.             template_folder = "./dist")
      复制代码
      1. cors = CORS(app, resources={"/api/*": {"origins": "*"}})
      复制代码
    改好之后,你就可以从前端的开发环境调用 Flask API 接口了。
    太神奇了 !
    现在你拥有了一个用你喜爱的技术完成的全栈应用。




    [h3]后记[/h3]最后我想说说如何改进这个方案。
    首先,在你代码里所有使用到的环境变量。主要是关于使用
    1. FLASK_DEBUG
    复制代码
    变量。我们在 CORS 设置中使用到它。例如,如果服务运行在开发环境设置
    1. FLASK_DEBUG=1
    复制代码
    你可以允许任何的请求源。如果不是,禁用 CORS 或者只允许可信源请求。
    另外一个改进是避免在客户端硬编码 API 路由。也许你需要思考为 API 接口创建映射表。所以当你改变 API 路由,你所需要做的只是更新映射表。前端的调用接口将不需要改变。
    还有个小建议 - 我通常同时开启至少3个终端窗口:一个运行 Flask,二个运行 Vue.js(第一个运行 Node.js 服务,第二个用来做项目构建打包)。
    源代码:https://github.com/oleg-agapov/flask-vue-spa
    [h3]最后非常感谢你的阅读![/h3]合作推广


    好文章,我在看
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:25
帖子:5
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP