抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

本文将一个Vue框架打包的资源挂载到Fastapi上,解决了assets文件404导致Vue页面白屏问题。

前言

学校一个大作业要求开发一个系统,拥有web界面以及对用户上传文件进行数据处理。

web前端打算选择使用Vue框架,后端框架使用简单易上手的Fastapi。

现在把搭建的流程走一遍。

搭建前端框架

在目录里面使用Vue官方脚手架创建项目。(请确保电脑上已安装Node.js,如未安装则无法使用npm)

1
2
3
4
5
6
7
8
9
10
11
12
npm create vue@latest
Vue.js - The Progressive JavaScript Framework

√ 请输入项目名称: ... frontend
√ 是否使用 TypeScript 语法? ... 否 / 是
√ 是否启用 JSX 支持? ... 否 / 是
√ 是否引入 Vue Router 进行单页面应用开发? ... 否 / 是
√ 是否引入 Pinia 用于状态管理? ... 否 / 是
√ 是否引入 Vitest 用于单元测试? ... 否 / 是
√ 是否要引入一款端到端(End to End)测试工具? » 不需要
√ 是否引入 ESLint 用于代码质量检测? ... 否 / 是
√ 是否引入 Vue DevTools 7 扩展用于调试? (试验阶段) ... 否 / 是

image.png

这里项目命名为frontend,除了使用Vue RouterPinia之外其他都选否。

(当然也可酌情选择,其他选项可让代码更规范,不过由于独立开发以效率优先就没选)

接下来按照说明执行一下这三条命令。

如卡在npm install这步可在网上了解一下如何设置npm镜像源(注意内容时效性,例如淘宝源修改域名,某些源由公开转为私有)

1
2
3
cd frontend
npm install
npm run dev

输入完npm run dev后可以打开http://localhost:5173/,此时即可看到效果。

imageecdc6b1042f319a9.png

搭建后端框架

在目录下新建文件夹backend用于存放后端文件,进入backend文件夹。

为了防止Python环境污染,通过venv创建一个虚拟环境并进入。

1
2
python -m venv .venv
.\.venv\Scripts\activate

注意venv环境无法指定Python版本,venv环境会使用创建它的python版本。

进入虚拟环境后可输入python -V查看版本。

我的Python版本为3.10.12。

安装fastapi框架。

1
pip install fastapi

稍等片刻后安装完成,在backend目录下新建main.py文件作为fastapi的入口。

1
2
3
4
5
6
7
8
# ./backend/main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"Hello": "World"}

随后在终端输入uvicorn main:app --reload --port 8001启动应用。

在浏览器打开http://127.0.0.1:8001即可看到效果。

http://127.0.0.1:8001/docs可查看fastapi默认生成的文档


此时项目目录如下图:

image18461075f0b94a6c.png

前端获取后端数据

前端需要加个包axios用来向后端发送数据。

后端需要设置CORS来允许跨域访问。

前端部分

回到.\frontend,输入npm install axios来安装包。

安装后修改.\frontend\src\main.js来导入这个包。

1
2
3
4
5
6
7
8
9
10
11
12
// frontend\src\main.js
...
import App from './App.vue'
import router from './router'

import axios from 'axios'

axios.defaults.baseURL = 'http://localhost:8001/api/'
axios.defaults.withCredentials = true

const app = createApp(App)
...

修改.\frontend\src\app.vue来向后端发送请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script setup>
import { RouterLink, RouterView } from 'vue-router';
import HelloWorld from './components/HelloWorld.vue';
import { ref } from 'vue';
import axios from 'axios';

const message = ref('Empty message');

const handleClick = () => {
axios.get('/button').then((res) => {
message.value = res.data;
});
}
</script>

<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />

<div class="wrapper">
<HelloWorld :msg="message" />
<button @click="handleClick">click me!</button>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>

<RouterView />
</template>
...

此时点击按钮并不会发生什么反应,我们在console里可以发现出现了什么问题。

imaged33a8a7d0edc9bb3.png

上述错误信息提示跨域访问不成功,这需要在服务器后端来配置。

后端部分

修改main.py添加CORS即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# backend\main.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins= "http://localhost:5174/",
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

@app.get("/")
def read_root():
return {"Hello": "World"}

@app.get("/api/button")
def read_button():
return "You clicked the button!"

此时点击按钮即可获取到消息。

image5352631c74a5e243.png

打包Vue文件

对于目前全栈来说每次都要分别启动前端服务和后端服务未免有些繁琐,因此我们选择将Vue文件打包成html和若干个jscss文件,通过静态文件挂载到fastapi上,这样只需要启动后端就可以了。

修改build路径

来到frontend\vite.config.js里修改一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// frontend\vite.config.js
import { fileURLToPath, URL } from 'node:url'

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

export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
// 新增
build: {
outDir: '../backend/static',
}
})

接着npm run build即可打包。

挂载静态文件

再回到后端main.js挂载一下。

将对应内容修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# backend\main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles

app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins= "http://127.0.0.1:8001/", # 注意此处修改为127.0.0.1:8001
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount('/assets', StaticFiles(directory='static/assets'), name='assets')

@app.get("/")
def root():
return FileResponse('static/index.html')

@app.get("/api/button")
def read_button():
return "You clicked the button!"

许多教程并没有mount assets文件夹,导致页面白屏

最后这一句话导致找了几天的bug,不过总算把这个框架搭建完了。

后续开发也许还要注意一下资源的路径问题。

评论