如何使用Docker设置和构建带有Flask后端的Vue应用程序

909 阅读12分钟

如何使用Docker设置和构建带有Flask后端的Vue应用程序

Vue.js是一个用于构建Web用户界面的JavaScript前端框架。Vue通常被用来构建基于单页的、运行在客户端的应用程序。

然而,Vue也可以用来构建一个全栈式的网络应用程序,其他后端技术如Node.js和Flask也会发挥作用。这是通过向服务器发出HTTP请求并将其填充到基于Vue的界面中来实现的。

Vue是伟大的,由于它有很好的概述功能。这包括使用虚拟DOM的能力,容易与其他技术(如Node.js和Python)集成,以及高运行时间性能。

另一方面,Flask是一个用Python编写的基于Web应用的微型框架,用于操作基于服务器的数据。Flask被设计成能够快速和容易地入门,并能够扩展到复杂的应用程序。

当构建一个全栈应用程序时,Vue和Flask可以作为一个单一的应用程序使用和运行。这使你有能力操纵网页的外观,并使用Flask处理基于服务器的数据。

当一起运行这些不同的技术时,如果总是设置本地环境来运行全栈应用,可能会变得很麻烦。

因此,Docker扮演了一个非常重要的角色。它允许你建立这样一个应用程序,并通过容器虚拟运行它们。这意味着任何本地环境都不会影响你的应用程序的运行。

Docker会将Vue和Flask旋转起来,将它们容器化并作为一个整体运行。你所要做的就是设置简单的指令,解释应用程序运行所需的不同依赖性。这包括Python的版本和你想运行Flask的库。

在本指南中,我们将使用Vue和Flask构建一个应用程序,并使用Docker运行它。我们将使用SQLite作为应用程序的数据库。

前提条件

要跟上这篇文章,必须具备以下条件。

  1. 在你的电脑上安装了[Python]。
  2. 有一些关于Flask的工作知识。
  3. 在你的电脑上安装[Node.js]。
  4. 对Vue.js有一定的了解。
  5. 在你的电脑上安装了[Docker]。

使用Flask建立服务器端环境

对于服务器端,我们将使用Flask和SQLite(一个轻量级的SQL数据库)建立一个REST API。

为了实现这一目标,我们将遵循以下步骤。

首先创建一个项目文件夹,用来创建Flask REST API,命名为:flask-todos-rest-api

为了设置我们的Flask环境,我们将使用[pipenv]。

要检查你是否安装了pipenv ,运行以下命令。

python -m pipenv --version

如果你没有安装,运行以下命令。

pip install pipenv

通过运行以下命令来初始化环境。

python -m pipenv shell

安装软件包

我们将使用以下软件包。

  • [Flask]:为应用程序提供架构设置的框架。
  • [Flask-sqlalchemy]:提供有用的默认值和额外的帮助器,使其更容易完成数据库任务。
  • [Flask-marshmallow]:构建API时有用的Flask的薄集成层。
  • [Flask-cors]:用于处理跨来源的资源访问。

要安装上述所有软件包,请运行此命令。

python -m pipenv install flask flask-sqlalchemy flask-marshmallow marshmallow-sqlalchemy flask-cors

使用Flask设置服务器端应用程序

要设置服务器端应用程序,在你的flask-todos-rest-api 文件夹中创建一个app.py 文件。

在这个文件中,通过添加以下几行代码设置一个基本的Flask应用程序。

from flask import Flask

## Init app

app = Flask(__name__)

# Start the app
if __name__ == '__main__':
    app.run(debug=True)

通过上面的命令,我们正在导入Flask模块,初始化它,并启动它。

设置数据库

要设置SQLite数据库,首先要导入以下包。

from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os

然后设置应用程序的基础目录。

basedir = os.path.abspath(os.path.dirname(__file__))

添加数据库应用配置。

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir,'db.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

由于我们使用的是Flask-sqlalchemy和Flask-marshmallow,我们现在可以设置SQLAlchemy 来初始化数据库Marshmallow 来初始化marshmallow,如下图所示。

db = SQLAlchemy(app)

ma = Marshmallow(app)

现在数据库的配置已经设置好了,我们可以开始设置todo模型了。这将构成一个存储在SQLite数据库中的样本todo列表。

class Todo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100))
    description = db.Column(db.String(400))

    def __init__(self,title,description):
        # Add the data to the instance
        self.title = title
        self.description = description

从上面来看,我们定义了一个Todo将有一个ID、标题和描述。

由于我们使用的是SQLite数据库,我们需要设置一个模式来存储我们的todo。当查询todos数据时,该模式将被调用。

class TodoSchema(ma.Schema):
    class Meta:
        fields = ('id','title','description')

上面,我们定义了每个todo,我们将对id、title和description感兴趣。

为了初始化上述模式,我们必须对单个todo和多个todos采取不同的方法。

为此,我们将添加以下内容。

todo_schema = TodoSchema()
todos_schema = TodoSchema(many=True)

第一个是针对单个todo,另一个是针对多个todos。

设置SQLite数据库和表

从你的代码编辑器中打开终端,运行以下命令,启动一个交互式的python环境。

python -m pipenv run python
  • 从shell中运行下面的命令。
from app import db # import db

db.create_all() # create database and tables
  • 关闭交互式shell。
exit()

设置路由

要设置路由,首先要导入软件包。

from flask import Flask,request, jsonify
from flask_cors import CORS,cross_origin

request 将用于获取有效载荷(发送的数据),而 将用于返回JSON数据。 和 用于设置访问策略。jsonify CORS cross_origin

然后添加CORS配置,以处理跨来源的人进来消费这个API。

CORS(app,resources={r"/api": {"origins": "*"}})
app.config['CORS_HEADERS'] = 'Content-Type'

从上面来看,我们接受所有进入/api 端点的来源,我们将从该端点公开API。

现在让我们添加所有必要的路由来处理CRUD操作。

创建一个todo

下面的路由创建一个todo。

@app.route('/api/todo', methods=['POST'])
@cross_origin(origin='*',headers=['content-type'])
def add_todo():
# get the data
    title = request.json['title']
    description = request.json['description']

    # Create an instance
    new_todo = Todo(title, description)

    # Save the todo in the db
    db.session.add(new_todo)
    db.session.commit()

# return the created todo
    return todo_schema.jsonify(new_todo)

从上面的路由中,我们接受所有来源,从有效载荷中接收todo的标题和描述,将其保存到数据库中,并返回保存的todo。

获取所有todos

下面的路由获取所有todos。

# Get all todos
@app.route('/api/todo', methods=['GET'])
@cross_origin(origin='*',headers=['Content-Type'])
def get_todos():
    # get the todos from db
    all_todos = Todo.query.all()
    # get the todos as per the schema
    result = todos_schema.dump(all_todos)
    # return the todos
    return jsonify(result)

从上面来看,我们接受所有的起源,获取所有保存的todos,并返回它们。

获取一个单一的路由

下面的路由获取一个单一的路由。

# Get a single todo
@app.route('/api/todo/<id>', methods=['GET'])
@cross_origin(origin='*',headers=['Content-Type'])
def get_todo(id):
    # get a single todo
    todo = Todo.query.get(id)
    # return the todo as per the schema
    return todo_schema.jsonify(todo)

从上面来看,我们从URL中接受todo的id,接受所有的起源,获取那个特定的todo,并返回它。

更新一个todo路由

下面的路由更新一个todo。

# update a todo
@app.route('/api/todo/<id>', methods=['PUT'])
@cross_origin(origin='*',headers=['Content-Type'])
def update_todo(id):
    # get the todo first
    todo = Todo.query.get(id)
    # get the data
    title = request.json['title']
    description = request.json['description']

    # set the data
    todo.title = title
    todo.description = description
    
    # commit to the database
    db.session.commit()

    # return the new todo as per the schema
    return todo_schema.jsonify(todo)

在上面,我们接受要更新的todo的ID,接受所有的来源,获得特定的todo和数据,设置新的数据,保存到数据库,并返回保存的数据库。

删除一个todo路线

下面的路由可以删除一个todo。

# Delete a todo
@app.route('/api/todo/<id>', methods=['DELETE'])
@cross_origin(origin='*',headers=['Content-Type'])
def delete_todo(id):
    # get the todo to be deleted
    todo = Todo.query.get(id)

    # delete from the database
    db.session.delete(todo)

    # commit on the database
    db.session.commit()

    # return thr deleted todo as per the schema
    return todo_schema.jsonify(todo)

上面的路由接受要删除的todo的id,接受所有的起源,得到todo,从数据库中删除它,并返回被删除的todo。

设置完路由后,通过运行以下命令启动你的应用程序。

python -m pipenv run python app.py

一切都应该正常,开发服务器也应该启动了。如果你遇到了错误,请重温一下步骤。

你的控制台输出应该类似于。

flask-console-output

使用Vue设置客户端

要设置客户端,首先要使用Vue CLI创建一个骨架应用程序。

要检查你是否安装了CLI,请使用以下命令。

vue --version

如果你没有安装CLI,用下面的命令安装它。

npm install -g @vue/cli

使用下面的命令创建骨架应用程序。

vue create todos-flask-app

对于后面的问题,请随意使用默认值或自己的选择。

我们还将添加一些额外的包来处理服务器端的路由。

这些包是。

  • Axios:用于处理客户端/服务器端的请求。
  • Vue-router:用于处理导航。
npm install axios vue-router

设置Vue前台应用程序

安装完软件包后,我们需要在src/main.js 中对其进行配置,如下所示。

import axios from 'axios'
import VueRouter from 'vue-router'

Vue.config.productionTip = false
Vue.prototype.$http = axios;
Vue.use(VueRouter);

src/App.vue ,编辑<template> ,如下所示。

<div id="app">
    <head>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
        <title>Todos</title>
    </head>

    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="/"> Todos app </a>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mx-auto">
                <li class="nav-item" :class="home_class">
                    <a class="nav-link" href="/"> Home </a>
                </li>
                <li class="nav-item" :class="add_todo_class">
                    <a class="nav-link" href="/add-todo"> Add todo </a>
                </li>
            </ul>
        </div>
    </nav>

    <router-view> </router-view>
</div>

我们在外部链接bootstrap的CSS来处理我们的造型,添加一个简单的导航栏,并在导航不同页面时添加动态内容区。

编辑JavaScript,如下所示。

<script>
    export default {
        data() {
            return {
                home_class: this.$route.path === "/" ? "active" : "",
                add_todo_class: this.$route.path === "/add-todo" ? "active" : "",
            };
        },
    };
</script>

在上面的片段中,我们正在设置导航栏的动态类。

编辑样式如下。

<style>
    #app {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
    }
</style>

上面我们正在为应用程序组件添加自定义样式。

Todos列表卡

src/components 文件夹中,创建一个Todos.vue 文件。

在该文件中,添加以下HTML。

<template>
    <div class="todos">
        <div class="container">
            <div class="row">
                <div class="col-sm-6 offset-sm-3">
                    <!-- Showing the added todos -->

                    <div v-if="todos.length == 0">
                        <div class="card mt-2 mb-2">
                            <div class="card-body">
                                <h4 class="card-title">You do not have any saved todo</h4>
                                <div class="d-flex justify-content-between">
                                    <a class="btn btn-info text-white" href="/add-todo">Add todo</a>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div v-else-if="todos.length > 0" v-for="todo in todos" v-bind:key="todo.id">
                        <div class="card mt-2 mb-2">
                            <div class="card-body">
                                <h4 class="card-title">{{todo.title}}</h4>
                                <p class="card-text">{{todo.description}}</p>
                                <div class="d-flex justify-content-between">
                                    <button class="btn btn-info text-white" @click="editTodo(todo.id)">
                                        Edit
                                    </button>
                                    <button class="btn btn-danger" @click="deleteTodo(todo.id)">
                                        Delete
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

在这里,我们正在检查我们是否有todos;如果没有,我们会显示一个信息。否则,我们将循环浏览它们,输出每一个。

添加下面的JavaScript。

<script>
    export default {
        // component data
        data() {
            return {
                todos: [],
            };
        },
        methods: {
            // fetching todos
            async getData() {
                try {
                    const response = await this.$http.get(
                        "http://localhost:5000/api/todo"
                    );
                    this.todos = response.data;
                } catch (error) {
                    console.log(error);
                }
            },
            // editing a todo
            async editTodo(todoId) {
                // Push to the edit todo page
                this.$router.push({
                    path: `/edit-todo/${todoId}`,
                });
                return;
            },
            // deleting a todo
            async deleteTodo(todoId) {
                // confirm with the user
                let confirmation = confirm("Do you want to delete this todo?");

                if (confirmation) {
                    try {
                        await this.$http.delete(`http://localhost:5000/api/todo/${todoId}`);
                        // refresh the todos
                        this.getData();
                    } catch (error) {
                        console.log(error);
                    }
                }
            },
        },

        // Fetch the todos on load
        created() {
            this.getData();
        },
    };
</script>

这里我们输出组件加载时获取的todos,编辑和删除todo的功能。

添加以下样式。

<style scoped>h3 {
    margin: 40px 0 0;
}

ul {
    list-style-type: none;
    padding: 0;
}

li {
    display: inline-block;
    margin: 0 10px;
}

a {
    color: #42b983;
}

.card-body {
    text-align: left;
}

.todos {
    margin-top: 10px;
}

</style>

上面的代码片断是添加到我们的todo组件的简单样式。

添加一个todo表单

创建一个AddTodo.vue 文件并添加以下组件。

HTML

<template>
    <div class="container">
        <div class="row">
            <div class="col-sm-6 offset-sm-3">
                <form id="todo-form" method="post" @submit.prevent="checkForm" novalidate="true">
                    <div v-if="todo.error" class="form-group mt-1">
                        <div class="alert alert-danger">{{todo.error}}</div>
                    </div>
                    <div v-if="todo.message" class="form-group mt-1">
                        <div class="alert alert-success">{{todo.message}}</div>
                    </div>
                    <div class="form-group mt-3" style="text-align: left">
                        <label for="title">Title</label>
                        <input v-model="todo.title" type="text" class="form-control" id="title" placeholder="Enter todo's title" />
                        <small id="titleHelp" class="form-text text-muted">E.g taking a walk.</small>
                    </div>
                    <div class="form-group mt-3" style="text-align: left">
                        <label for="description">Description</label>
                        <textarea v-model="todo.description" class="form-control" name="description" id="description" placeholder="Todo's description"></textarea>
                        <small id="descriptionHelp" class="form-text text-muted">E.g A long walk around the estate.</small>
                    </div>
                    <div class="form-group mt-3">
                        <button type="submit" class="btn btn-primary btn-lg btn-block">
                            Submit
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</template>

这里我们展示的是一个由JavaScript填充字段的表单。

JavaScript

<script>
    export default {
        data() {
            return {
                todo: {
                    title: "",
                    description: "",
                    error: null,
                    message: null,
                },
            };
        },
        methods: {
            checkForm: async function(e) {
                if (this.todo.title && this.todo.description) {
                    try {
                        // send data to the server
                        await this.$http.post("http://localhost:5000/api/todo", {
                            title: this.todo.title,
                            description: this.todo.description,
                        });

                        //reset the fields
                        this.todo.title = "";
                        this.todo.description = "";

                        // set the message
                        this.todo.message = "Todo added successfully";

                        return;
                    } catch (error) {
                        this.todo.error = error;
                        return;
                    }
                }
                this.todo.error = null;
                if (!this.todo.title) {
                    this.todo.error = "Title is required";
                    return;
                }
                if (!this.todo.description) {
                    this.todo.error = "Description is required";
                    return;
                }
                e.preventDefault();
            },
        },
    };
</script>

从上面的脚本中,我们将从组件中导出数据,以及一个处理表单提交时的验证和数据提交的方法。

添加一个编辑待办事项表单

创建一个EditTodo.vue 文件并添加以下内容。

<template>
    <div class="container">
        <div class="row">
            <div class="col-sm-6 offset-sm-3">
                <form id="todo-form" method="post" @submit.prevent="checkForm" novalidate="true">
                    <div v-if="todo.error" class="form-group mt-1">
                        <div class="alert alert-danger">{{todo.error}}</div>
                    </div>
                    <div v-if="todo.message" class="form-group mt-1">
                        <div class="alert alert-success">{{todo.message}}</div>
                    </div>
                    <div class="form-group mt-3" style="text-align: left">
                        <label for="title">Title</label>
                        <input v-model="todo.title" type="text" class="form-control" id="title" placeholder="Enter todo's title" />
                        <small id="titleHelp" class="form-text text-muted">E.g taking a walk.</small>
                    </div>
                    <div class="form-group mt-3" style="text-align: left">
                        <label for="description">Description</label>
                        <textarea v-model="todo.description" class="form-control" name="description" id="description" placeholder="Todo's description"></textarea>
                        <small id="descriptionHelp" class="form-text text-muted">E.g A long walk around the estate.</small>
                    </div>
                    <div class="form-group mt-3">
                        <button type="submit" class="btn btn-primary btn-lg btn-block">
                            Submit
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</template>

add todo 表单类似,我们将输出一个edit todo 表单,该表单预先填充了要编辑的特定todo的JavaScript的数据。

JavaScript

<script>
    export default {
        data() {
            return {
                todo: {
                    loading: false,
                    title: "",
                    description: "",
                    error: null,
                    message: null,
                    id: this.$route.params.id,
                },
            };
        },

        methods: {
            getTodo: async function() {
                // the current todo id
                let todoId = this.todo.id;
                // start loading
                this.todo.loading = true;
                // get the todo
                try {
                    let response = await this.$http.get(
                        `http://localhost:5000/api/todo/${todoId}`
                    );
                    this.todo.title = response.data.title;
                    this.todo.description = response.data.description;
                    this.todo.loading = false;
                    return;
                } catch (error) {
                    this.todo.error = error;
                    return;
                }
            },
            checkForm: async function(e) {
                // Custom validation
                if (this.todo.title && this.todo.description) {
                    try {
                        // send data to the server
                        await this.$http.put(
                            `http://localhost:5000/api/todo/${this.todo.id}`, {
                                title: this.todo.title,
                                description: this.todo.description,
                            }
                        );

                        //reset the fields
                        this.todo.title = "";
                        this.todo.description = "";

                        // set the message
                        this.todo.message = "Todo edited successfully";

                        return;
                    } catch (error) {
                        this.todo.error = error;
                        return;
                    }
                }
                this.todo.error = null;
                if (!this.todo.title) {
                    this.todo.error = "Title is required";
                    return;
                }
                if (!this.todo.description) {
                    this.todo.error = "Description is required";
                    return;
                }
                e.preventDefault();
            },
        },
        created() {
            // Called on load
            this.getTodo();
        },
    };
</script>

上面我们正在导出todo数据,在页面加载时获得todo,处理自定义验证,以及提交编辑过的todo的数据。

在设置好这些组件后,我们需要处理进入各个页面的路由。

为了做到这一点,我们将在src/main.js 文件中添加以下内容。

导入AddTodo,EditTodoTodos 组件。

import AddTodo from "./components/AddTodo"
import EditTodo from "./components/EditTodo"
import Todos from "./components/Todos"

然后创建各种VueRouter 实例来处理上面的组件。

// create a vuerouter instance
const router = new VueRouter({
    mode: 'history',
    base: __dirname,
    routes: [{
            path: '/',
            component: Todos,
            name: 'home'
        },
        {
            path: '/add-todo',
            component: AddTodo,
            name: 'add-todo'
        },
        {
            path: '/edit-todo/:id',
            component: EditTodo,
            name: 'edit-todo'
        },
    ]
});

// pass the router to the app config
new Vue({
    router: router,
    render: h => h(App),
}).$mount('#app');

从上面来看,我们正在创建一个VueRouter 实例,传入mode,base, 和routes 。对于路由,我们为每个路由传递path,component, 和name

创建实例后,我们将其传递给Vue对象。这样,我们就可以启动开发服务器并测试我们实现的功能了。

要做到这一点,运行以下命令。

npm run serve

上述命令将启动开发服务器,端口为8080 。你可以从http://localhost:8080 访问你的应用程序。

你的应用程序应该类似于以下内容。

Todos页面

todos-page

添加Tododo页面

add-todo-page

对应用程序进行停靠

要对我们建立的应用程序进行dockerize,我们将遵循以下步骤。

Dockerize Flask API

要对API进行dockerize,需要在API文件夹中创建一个Dockerfile,以及一个.dockerignore 文件。Dockerfile将承载创建镜像时的指令,而.dockerignore 文件将承载复制到镜像时要忽略的文件。

为了让Flask应用程序在Docker中工作,我们需要确保我们使用的所有包都是可用的,并能被容器化的REST API访问。

为了使这些包能够被Docker访问,我们将把它们导入到一个requirements.txt 文件。然后,Docker将运行这个文件,并在运行API的容器中安装这些包。

在你的flask-todos-rest-api 目录中,运行这个命令。

pip freeze > requirements.txt

这将创建一个requirements.txt ,并导入我们所使用的所有包。

在Docker文件中添加以下内容。

# Base python package
FROM python:3.8-slim-buster

# Working directory
WORKDIR /app

# Copy the dependencies
COPY requirements.txt

# Install the dependencies
RUN pip3 install -r requirements.txt

# Copy the files
COPY . .

# Executable commands
CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

这里我们从外部导入Python包,定义工作目录,复制依赖关系,安装依赖关系,复制文件,并设置执行命令。

.dockerignore 文件中添加以下内容。

__pycache__/

.gitignore

Pipfile

Pipfile.lock

README.MD

从上面来看,我们正在添加所有不应该包含在docker镜像中的文件。

Dockerize Vue应用程序

为了对Vue应用程序进行dockerize,我们还将在项目文件夹中创建一个Dockerfile。与之前的实例类似,它将承载创建docker镜像时的说明。

在Dockerfile中,添加以下内容。

#Base image
FROM node:lts-alpine

#Install serve package
RUN npm i -g serve

# Set the working directory
WORKDIR /app

# Copy the package.json and package-lock.json
COPY package*.json ./

# install project dependencies
RUN npm install

# Copy the project files
COPY . .

# Build the project
RUN npm run build

# Expose a port
EXPOSE 5000

# Executables
CMD [ "serve", "-s", "dist" ]

从上面来看,我们要导入节点镜像,设置工作目录,复制package.json ,和package-lock.json ,安装项目的依赖性,复制项目文件,构建项目,暴露一个端口,并设置可执行文件。

设置一个整体的docker-compose文件

在为每个文件夹即api-folder ,和client-folder ,设置了一个Docker文件后,我们将在这两个文件夹外设置一个docker-compose.yml 文件。

首先,在API和客户端文件夹外创建一个docker-compose.yml 文件。

docker-compose.yml 文件中,添加以下内容。

version: '3.8'

services:
    flask-todos-api:
        build: ./flask-todos-rest-api
        ports: 
            - 5000:5000

    vue-todos-app:
        build: ./todos-flask-app
        ports: 
            - 8080:5000

这里我们定义了docker-compose 的版本,并设置了两个服务。对于每个服务,我们定义构建(存放Docker文件的文件夹)和端口(项目要运行的地方)。为了使服务不在平行端口上发生冲突,client-side 将在端口8080 上运行。

构建Docker镜像

为了构建Docker镜像,从docker-compose.yml 文件的位置,运行以下命令。

docker-compose up -d --build

上述命令将构建Docker镜像。

启动Docker容器

要从与上一步相同的位置启动Docker容器,请运行以下命令。

docker-compose up

上述命令将启动两个服务。两个服务启动后,继续进入http://localhost:8080,与应用程序进行交互。

与应用程序交互后,你可以按CRTL + C ,停止容器。你还可以与朋友分享docker镜像,以展示你所建立的东西。

总结

在这篇文章中,我们已经创建了一个Vue.js应用,该应用消耗了一个可靠的Flask API。