翻译进度
15
分块数量
4
参与人数

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

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。

file

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

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

Loner1024 翻译于 1个月前

我想做个不一样的。 做一个用 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-spa

josephInAfrica 翻译于 1个月前

客户端

为了生成基本的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
leamen 翻译于 2个月前

下一步:

$ 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>
leamen 翻译于 2个月前

We will use them to correctly recognize our current location (according to address bar). Now we need to change frontend/src/router/index.js file in order to render our new components:

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'
})

If you try to enter localhost:8080 and localhost:8080/about you should see corresponding pages.

file

We are almost ready to build a project in order to create a bundle with static assets. Before that let's redefine output directory for them. In frontend/config/index.js find next settings

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

and change them to

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

So /dist folder with html/css/js bundle will have the same level as /frontend. Now you can run $ npm run build to create a bundle.

gIwSvo62o6.png!large

Back-end

For Flask server I'll use python version 3.6. Inside root /flaskvue folder create new sub-folder for back-end code and initialize virtual environment there:

$ mkdir backend
$ cd backend
$ virtualenv -p python3 venv

To enable virtual environment run (on macOs):

$ source venv/bin/activate

For activation in Windows use this docs.

Under virtual environment install Flask with:

(venv) pip install Flask

Now let's write code for Flask server. Create a file run.py in root directory:

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

Add next code to this file:

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")

This code is slightly differs from Flask starter "Hello world" code. The major difference is that we specify static and templates folder to point to /distfolder with our front-end bundle. To run Flask server run in root folder:

(venv) FLASK_APP=run.py FLASK_DEBUG=1 flask run

This will start a web server on localhost:5000FLASK_APP points to server startup file, FLASK_DEBUG=1 will run it in debug mode. If everything is correct you'll see familiar Home page you've done on in 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 将会自己处理其余的事情。

leamen 翻译于 2个月前

添加 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”的消息提示了.

leamen 翻译于 2个月前

Adding API endpoint

The very last example of my Vue.js/Flask tutorial will be creation of API on server side and dispatching it on client-side. I'll create a simple endpoint which will return a random number from 1 to 100.

Open run.py and add:

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")

First I imported random library and jsonify function from Flask library. Then I added new route /api/random to return JSON like this:

{
  "randomNumber": 36
}

You can test this route by navigating to localhost:5000/api/random.

At this point server-side work is done. Time to show this on client-side. I'll change Home.vue component to show my random number:

<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>

At this stage I just emulate random number generation process on client-side. So, this component works like this:

  • on initialization variable randomNumber is equal to 0
  • in methods section we have getRandomInt(min, max) function which will return a number from specified range, getRandom function will dispatch previous function and assign it value to randomNumber
  • after creation of component method getRandom will be invoked to initialize randomNumber
  • on button click event we will dispatch getRandom method to get new number

Now on home page you should see our random number generated by front-end. Let's connect it to back-end.

For that purpose I will use axios library. It allows us to make HTTP requests and return JavaScript Promise with JSON answer. Let's install it:

(venv) cd frontend
(venv) npm install --save axios

Open Home.vue again and add a few changes to <script> section:

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)
    })
  }
}

At the top we need to import axios library. Then there is a new method getRandomFromBackend which will use axios to asynchronously reach API and retrieve the result. And finally, method getRandom now should use getRandomFromBackend function to get a random value.

Save a file, go to browser, run a dev server again, refresh localhost:8080and... You should see an error in console and no random value. But don't worry, everything is working. We got CORS error which means that our Flask server API by default is closed to other web-servers (in our case it's Node.js server running our Vue.js app). If you create a bundle with npm run buildand open localhost:5000 (so Flask server) you will see working application. But it's not very convenient to create a bundle every time you made some changes to client-side application.

Let's use CORS plugin for Flask which will allow us to create a rules for API accesses. Plugin is called flask-cors , let's install it:

(venv) pip install -U flask-cors

You can read documentation on better explanation of what ways you have to enable CORS on your server. I'll use resource specific method and apply {"origins": "*"} to all /api/* routes (so everyone can use my /apiendpoints). In run.py:

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

With that change in place you can call Flask APIs right from front-end development server.

Update:

Actually, there is no need for CORS extension if you will serve static files through Flask. Thanks to Carson Gee for this trick.

The idea is next. If the application is in debug mode it will just proxying our front-end server. Otherwise (in production) serve the static files. Here is how we can do that:

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")

Simple and elegant. Magic ✨!

Now you have a full-stack application built with your favorite technologies.

Q9mPWiSqp9.png!largeid7NjI9Gdd.png!large

Afterword

In the end I want to say a few words on how you can improve in this solution.

First of all get use CORS extension only if you want to give access to your API endpoints for external servers. Otherwise just use a trick with proxying front-end development server.

Another improvement will avoiding hard coded API routes on client side. Maybe you need to think of some dictionary with API endpoints. So when you'll change you API route all you need to do is just to refresh a dictionary. Front-end will still have a valid endpoint.

Usually during development you will have at least two terminal windows: one for Flask and another for Vue.js. In production you'll get rid of running separate Node.js server for Vue.

Source code: https://github.com/oleg-agapov/flask-vue-spa

Thank you for reading!

本文章首发在 LearnKu.com 网站上。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://codeburst.io/full-stack-single-p...

译文地址:https://learnku.com/python/t/24985

参与译者:4
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!