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
- Creating a Vue front-end app template with Vite
- Integrating with Flask
- 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
A directory named vite-project
will be created. Navigate into it and run the yarn
command:
cd vite-project
yarn
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",
},
});
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")
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
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" />
<img src="/static/vite.svg" class="logo" alt="Vite logo" />
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}"
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>
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}`);
});
};
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>
Verify operation with:
yarn build
flask run
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
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
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")
In Docker, this can be simplified to:
app = Flask(__name__)
Copy app.py
to the public
directory and run yarn build
:
cp app.py public
yarn build
Build the Docker image again with:
docker-compose build
Launch the container with:
docker-compose up -d
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}"
Create an args.json
file with the following contents:
args.json
{
"static_folder": "dist/static",
"template_folder": "dist",
"static_url_path": "/static"
}
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)
Flask+Vite is an underrated combo. Less enthusiastic about Vue to be honest? But of course YMMV.
The article is updated in this article.
dev.to/atsu/web-development-with-v...
Intresting tekst
Some comments may only be visible to logged-in visitors. Sign in to view all comments.