DEV Community

ATSU
ATSU

Posted on • Edited on

Web Development with Vite, Vue, and Flask

Introduction

We'll explore how to use Vue.js and Flask together to create an application. We'll cover the process of creating a front-end template using the development tool Vite and subsequently integrating a Flask back-end. This process is quite engaging, and I believe you'll find it intriguing!

Benefits
Combining Vue.js for the front-end and Flask for the back-end in web application development offers several advantages, especially when leveraging their respective strengths.

Python excels in handling backend operations, data manipulation, and complex mathematical calculations. Its vast ecosystem of libraries allows for easy integration with databases, data processing tools, and machine learning frameworks.

Vue.js, a progressive JavaScript framework, is designed to build modern, interactive user interfaces. JavaScript, being the language of the web, is well-suited for client-side development. Vue.js simplifies the creation of dynamic, responsive, and visually appealing UIs.

Overview

  1. Creating a Vue front-end app template with Vite
  2. Integrating with Flask
  3. Creating a Docker image

Creating Vue app template

Navigate to your development directory (e.g., Projects) and execute the following command:

yarn create vite

During the setup, you'll be prompted to answer several questions:

Project name: vite-project (can be any name, for example: 'vite-project')
Select a framework: Vue
Select a variant: JavaScript
Enter fullscreen mode Exit fullscreen mode

A directory named vite-project will be created. Navigate into it and run the yarn command:

cd vite-project
yarn
Enter fullscreen mode Exit fullscreen mode

At this point, you can already run the template application with:

yarn dev

When you visit http://localhost:5173 in a web browser, you'll see the application's interface. There's a button in the center of the screen labeled "count is 0." Clicking this button will increment the count.

Next, build the app.

yarn build

The source will be compiled, and the artifacts will be placed in the dist distribution directory. To perform a final check of the application, execute:

yarn preview

Accessing http://localhost:4173 in a browser, you should see the application interface. The app's behavior will be identical to what was seen with 'yarn dev', but with the compiled JavaScript running the show.

The typical front-end creation process involves modifying the source code, checking its functionality with yarn dev, and if there are no issues, executing yarn build. The final operation is verified with yarn preview, and this cycle continues.

Working with Flask

Initially, let's use Flask simply to display the application's interface. Flask defines where to place different types of resources, necessitating file placement according to these rules:

Parameter Name Description Default
static_folder JS, CSS, Location of static resources such as images static
template_folder Location of HTML files to be rendered by Flask templates
static_url_path Path name for accessing the static folder /static

Comparing the actual artifacts in the dist folder layout with the default values, some differences are apparent. Specifically, JS and CSS files are placed in the dist/assets folder, but we aim to align the layout with Flask's requirements by renaming assets to static in the configuration.

To do this, open the vite.config.js file and add the following setting to build.assetsDir:

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],

  build: {
    assetsDir: "static",
  },
});
Enter fullscreen mode Exit fullscreen mode

After running yarn build, confirm that the output has changed to dist/static.

Now, Flask takes the stage:

from flask import Flask, render_template

app = Flask(__name__, static_folder="dist/static", template_folder="dist", static_url_path="/static")


@app.route("/", defaults={"path": ""})
@app.route("/<path:path>")
def index(path):
    return render_template("index.html")
Enter fullscreen mode Exit fullscreen mode

Due to the layout generated by Vite differing from Flask's default, parameters like static_folder are explicitly specified with actual values.

Run the Flask server with:

flask run

Accessing http://localhost:5000 in a browser should display the application interface. The Vite logo might not appear, but the count-up functionality should work fine.

The logo's absence is due to the image file's location. If you check the dist folder, vite.svg is found directly under dist, but as per the table, it needs to be in the static_folder (=dist/static).

To address this, move vite.svg from the public directory to static:

cd public
mkdir static
mv vite.svg static
Enter fullscreen mode Exit fullscreen mode

The reason for not manually handling files under dist is to ensure they are automatically placed during yarn build.

Files in the public directory are copied to the dist directory, maintaining the tree structure during the build.

Additionally, the source referencing vite.svg must be updated. Relevant locations include:

<link rel="icon" type="image/svg+xml" href="/static/vite.svg" />
Enter fullscreen mode Exit fullscreen mode
<img src="/static/vite.svg" class="logo" alt="Vite logo" />
Enter fullscreen mode Exit fullscreen mode

After running yarn build, vite.svg should be in dist/static. Re-run flask run and visit http://localhost:5000. This time, the logo should display correctly!

To further utilize Flask, let's have it handle the incrementing process. Add to app.py:

@app.route("/increment/<int:count>")
def increment(count):
    return f"{count + 1}"
Enter fullscreen mode Exit fullscreen mode

Modify the Vue side to request Flask to increment when the button is pressed. The relevant code is in
src/components/HelloWorld.vue

<div class="card">
  <button type="button" @click="count++">count is {{ count }}</button>
  <p>
    Edit
    <code>components/HelloWorld.vue</code> to test HMR
  </p>
</div>
Enter fullscreen mode Exit fullscreen mode

Replace @click=... with a function that requests Flask. Implement 'incrementCount' as follows:

const count = ref(0);

const incrementCount = () => {
  fetch(`/increment/${count.value}`)
    .then((response) => {
      if (response.ok) {
        return response.text();
      } else {
        throw new Error(`${response.status} ${response.statusText}`);
      }
    })
    .then((val) => {
      count.value = parseInt(val);
    })
    .catch((error) => {
      console.error(`Error: ${error.message}`);
    });
};
Enter fullscreen mode Exit fullscreen mode

Substitute the button press action with this function name:

<div class="card">
  <button type="button" @click="incrementCount">count is {{ count }}</button>
  <p>
    Edit
    <code>components/HelloWorld.vue</code> to test HMR
  </p>
</div>
Enter fullscreen mode Exit fullscreen mode

Verify operation with:

yarn build
flask run
Enter fullscreen mode Exit fullscreen mode

Creating a Docker Image

Having completed most of our objectives, let's also try creating a Docker image.

Use VSCode convenient feature to create a Dockerfile:

Open the folder vite-project in VScode.
(I assume you already have it open...)

Access the command palette (Ctrl+Shift+P) and search for 'Add Docker Files'.

Select Docker: Add Docker Files to Workspace.... (If it doesn't appear, you might need to install the Docker extension).

Select Application Platform.
Python Flask.

Choose the app's entry point(e.g. manage.py, app.py)
→ Choose app.py.

What port(s) does your app listen on? Enter a comma-separated list, or empty for no exposed port.
→ Enter a comma-separated list, or empty for no exposed port. Select the displayed number as it is. It is probably 5002, but you can change it to any number you like.

Include optional Docker Compose files.
Do you want to create docker-compose.yml as well? Select Yes.

Dockerfile Editing

Make the following changes to the Dockerfile:

Originally, we have:

WORKDIR /app
COPY . /app
Enter fullscreen mode Exit fullscreen mode

Change the COPY source to ./dist since we want the dist directory in the container. Also, move index.html from the dist directory to the templates directory to adhere to the standard Flask structure:

WORKDIR /app
COPY ./dist/ .
RUN mkdir -p templates && mv index.html templates
Enter fullscreen mode Exit fullscreen mode

Modifying and Placing app.py

In the Docker version, the Flask layout has been reverted to default, so the optional parameters specified in app.py should be removed:

app = Flask(__name__, static_folder="dist/static", template_folder="dist", static_url_path="/static")
Enter fullscreen mode Exit fullscreen mode

In Docker, this can be simplified to:

app = Flask(__name__)
Enter fullscreen mode Exit fullscreen mode

Copy app.py to the public directory and run yarn build:

cp app.py public
yarn build
Enter fullscreen mode Exit fullscreen mode

Build the Docker image again with:

docker-compose build
Enter fullscreen mode Exit fullscreen mode

Launch the container with:

docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

Go to http://localhost:5002.

Did it work?

Visit http://localhost:5002 and check if the app works correctly!

Finally, to avoid managing separate versions of app.py for Docker and non-Docker environments, consider using a configuration file to set parameters like static_folder based on the environment. This approach is simple yet effective.

The final app.py could look like this:

import json
from flask import Flask, render_template

args = dict()
try:
    with open("args.json") as f:
        args = json.load(f)
except FileNotFoundError:
    pass

app = Flask(__name__, **args)


@app.route("/", defaults={"path": ""})
@app.route("/<path:path>")
def index(path):
    return render_template("index.html")


@app.route("/increment/<int:count>")
def increment(count):
    return f"{count + 1}"
Enter fullscreen mode Exit fullscreen mode

Create an args.json file with the following contents:

args.json

{
  "static_folder": "dist/static",
  "template_folder": "dist",
  "static_url_path": "/static"
}
Enter fullscreen mode Exit fullscreen mode

By doing this, parameters can be environment-specific, and app.py can be centrally managed. If args.json is missing, Flask will catch the FileNotFoundError and default values will be used.

Top comments (3)

Collapse
 
ebcefeti profile image
E. B. Cefeti

Flask+Vite is an underrated combo. Less enthusiastic about Vue to be honest? But of course YMMV.

Collapse
 
atsu profile image
ATSU

The article is updated in this article.

dev.to/atsu/web-development-with-v...

Collapse
 
michaelcoder12 profile image
Michaelcoder12

Intresting tekst

Some comments may only be visible to logged-in visitors. Sign in to view all comments.