概述
传统网站由一个 web 框架完全承担,例如基于 nodejs 的 express,koa,基于 python 的 django,tornado。新型网站演变为用 vue 开发前端系统,使用 api 框架开发后端 api 请求接口的模式。本文就介绍一下后端使用 python 的 fastapi 框架开发 api 接口,前端使用 vue 框架开发界面的方式快速开发网站。
搭建 fastapi 服务
首先建立一个项目文件夹,并建立两个子文件夹 backend 和 frontend。后端的代码将放置在 backend 文件夹中,前端的代码放在 frontend 文件夹里。
安装 fastapi
1 | cd backend |
编写 api 接口文件 backend/app/init.py
1 | from typing import Optional |
运行后端服务
1 | uvicorn app:app --reload --host 0.0.0.0 |
验证是否运行成功
curl http://127.0.0.1:8000/items/5?q=somequery
查看自动生成文档
搭建 vue 前端
这里我们采用了 ant design pro vue 作为我们的脚手架。
1 | cd frontend |
完成后,前端系统就运行在 8000 端口了。但此时,我们看到的前端只是一个静态网站,并没有动态内容。所有和后端的交互都是通过 mock 方式模拟的。在正式环境中,这些请求需要和之前搭建的 fastapi 后端交互完成。
添加 hello world 页面
在 frontend/src/views/helloworld 目录下,加入我们的页面 HelloWorld.vue
1 | <template> |
将 hello 页面加入路由,修改 frontend/src/config/router.config.js,把 asyncRouterMap 修改为如下内容
1 | export const asyncRouterMap = [ |
此时,菜单的名字将显示 menu.hello,原因是我们没有在国际化中添加对 menu.hello 词条的翻译选项,我们修改 frontend/src/locales/lang/en-US.js 增加如下选项
1 | const locale = { |
同样修改 frontend/src/locales/lang/zh-CN.js
1 | const locale = { |
这样,我们就能看到正确的支持多语言的版本了。
添加对 API 请求
在 frondend/src/api/hello.js 中添加和 hello 相关的 api
1 | import { axios } from "@/utils/request"; |
为了调试方便,我们使用 mock 功能在没有后端联调情况下模拟后端操作, 修改 frontend/src/views/helloworld/HelloWorld.vue
1 | <template> |
这样,访问首页时就能够看到后台模拟 mock 接口传回的数据。
将 vue 编译,由 fastapi 托管
编译 vue 代码,生成的产品正式代码在 dist 目录下
1 | yarn install && yarn run lint --no-fix && yarn run build --mode production |
把编译好的 dist 代码全部移动到 backend 的 public 目录下。这个过程我们可以使用 bash 脚本自动化
1 |
|
此时,我们需要配置 fastapi 完成以下的路由
- 对/api 请求使用 fastapi 的处理逻辑
- 对/api 下的其他未实现请求返回 404
- 对所有其他请求尝试加载 public 目录下的资源文件,若失败,则加载 index.html,但路径仍保持请求路径。
这就是 vue 文件托管的 html5 history mode https://router.vuejs.org/guide/essentials/history-mode.html
修改 backend/app/init.py 为如下内容
1 | import os.path |
有几个要点:
- app.get 的参数是路径名,注意和顺序相关,优先匹配最早的符合。另外,如果要匹配的参数中包括/,那么需要在参数后加:path 修饰符
- 按照顺序分别匹配/api 路径,根目录,然后将所有未匹配请求发送给最后一个函数。如果找到静态文件则返回其内容,否则就直接用 index.html 的内容返回。vue 会处理具体的路由渲染规则。
- ant design pro 本身自带权限控制系统,但是需要注意的是,原有的 mock 文件中的响应是需要加上包装的,比如 result 等于 mock 中 builder 的参数。这个没有文档提及,这块浪费了很多调试时间。
- 还有一些接口没有实现,这个文件只是包含了刚好能够让登陆用户进入系统的最小 api 需要的接口
优化
至此,我们完成了一个由 fastapi 托管的 vue 开发的网页。但还有一个小问题:静态文件的托管。我们知道,vue 的资源文件通常都是非常大的,少则几 M,如果按照现在的 fastapi 接口,每次访问都会向服务器请求所有文件下载,那必然是非常低效的。我们希望 fastapi 能和 nginx 的静态文件托管那样,在静态文件没有变化时访问 not modified 响应,而且我们希望采用 gzip 压缩方式提高传输效率。
修改 vue 资源文件的存储位置
默认打包后 vue 的资源文件将根据文件类型,分别存储在 js,css 和 img 目录下,我们希望这些文件统一放在 public 目录下,方便我们在 fastapi 中配置对该目录下的所有文件都托管给静态文件管理模块。我们需要在 vue.config.js 文件中加入这个配置:
1 | // vue.config.js |
这样所有资源文件都会在 public 目录下。
然后我们在 fastapi 中修改对/public 路径下文件的服务
1 | app.mount("/public", StaticFiles(directory="public/public"), name="public") |
这样,fastapi 会提供资源文件的 etag 检查,防止重复下载。另外,可以设置 fastapi 支持 gzip
1 | from fastapi import FastAPI |
至此,优化过后刷新网站不会每次都从服务器重新下载资源文件了。
总结
总体而言,fastapi+vue 的部署方式简单高效,能满足一般应用类网站特别是中台的开发需求。但在开发过程中需要注意将 vue 的编译文件托管给 fastapi 时的相关配置。
项目地址:https://github.com/elprup/fastapi-vue-template
相关引用
golang+vue 的实现例子 https://github.com/Sloaix/Gofi