# 一、需求背景
原项目使用 tornado 框架动态渲染页面返回,现在需要慢慢引入前后端分离模式,考虑在新功能上用 Vue 实现页面,并且使新老页面可以相互跳转,共用同一套鉴权。
# 二、解决方案
# 1. 基本思路
给新页面分配唯一路由(比如
/example/
,以下配置以该路由举例 ),以该路由的开头的页面都会被 Nginx 通过路由正则匹配指向新的静态页面地址( Vue 打包之后的页面),其他路由仍旧保持原来的路由匹配走原来的 tornado框架。新老页面的相互跳转直接用
href='xxx'
实现。共用同一套鉴权的需求目前依赖使用同一cookie实现,在向后端请求时会自动带上cookie里的用户信息,只需要在后端接口加上鉴权装饰器即可,前端无需额外配置。
# 2. 实现方案
为实现以上思路(主要是第1点,其他较为简单),需做三步处理:
- 在 Nginx 里进行特殊配置,正则匹配唯一路由并转发(和普通静态页面部署有所区别)
- 把前端路由模式从兼容性较好的 hash 模式改成 history 模式
- 前端打包的 baseUrl 需要设置为 Nginx 上配置的给新页面的唯一路由
# 三. 具体实现流程
# 1. Nginx配置
想要实现的效果是匹配到/example/
路由后,这个路由就被前端接管,接下来任何操作都由前端路由来控制
添加以下配置至nginx.conf
# 匹配以/example/开头的路由,匹配后不匹配其他路由
location ^~ /example/ {
alias /example/dist/; # 使用 alias 指向准确目录之后由前端接管
index index.html;
autoindex on;
try_files $uri $uri/ /dist/index.html; # 前端 history 模式需加上该行配置,否则回车或刷新页面 404
}
配置后nginx.conf
文件结构如下
... # 全局块
events { # events块
...
}
http # http块
{
... # http全局块
server # server块
{
... # server全局块
location [PATTERN] # location块
{
...
}
# 添加的配置
location ^~ /example/ {
alias /example/dist/;
index index.html;
autoindex on;
try_files $uri $uri/ /dist/index.html;
}
location [PATTERN]
{
...
}
}
server
{
...
}
... # http全局块
}
# alias 和 root 的区别
alias的处理结果是:使用alias路径替换location路径
location /example/ { alias /home/www/example/; }
在上面alias虚拟目录配置下,访问
http://www.example.com/example/a.html
实际指定的是/home/www/example/a.html
。root的处理结果是:root路径+location路径
location /example/ { root /home/www/; }
使用该配置 Nginx 就会去
/home/www/example
下寻找http://www.example.com/example
的访问资源
# 2. 配置前端路由模式
前端路由模式分为2种,一种是 hash
模式,一种是 history
模式
区别 | hash 模式 | history 模式 |
---|---|---|
地址栏表现 | 带 # 号 | 标准的 Url 格式,不带 # |
路由的跳转实现 | window.onhashchange 监听 hash 的改变 | pushState 或者 replaceState API 改变路由 |
页面刷新 | 请求的地址中不携带 # 后面的内容,所以不需要后端的配合,也不会出现 404 | 请求的是当前地址的完整路径,需要服务器做特殊处理,否则会出现 404 |
兼容性 | 可以兼容一些低版本的浏览器 | 不兼容低版本浏览器 |
一般实际开发场景中,用 hash 模式较为常见,
一是因为兼容性好(history 模式是 Html5 后提出的,故不兼容低版本浏览器),
二是 hash 模式不需要在 Nginx 上特殊配置。
若需要Url整洁或是别的需求,则考虑 history 模式,一切以实际开发需求为准。
具体可见 Vue-router hash和history的区别
# 具体配置
目前我们采用 history 模式主要是因为原项目是标准的Url格式,此处做统一,配置如下:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
// 本地开发时因为example路由下没有页面,直接重定向到A页面
{
path: '/example',
redirect: '/example/a'
},
{
path: '/example/a',
name: 'ExampleA',
component: () => import('../views/ExampleA.vue'),
meta: {
title: 'A页面'
}
},
{
path: '/example/b',
name: 'ExampleB',
component: () => import('../views/ExampleB.vue'),
meta: {
title: 'B页面'
}
},
]
// 采用history模式
const router = createRouter({
mode: 'history',
history: createWebHistory(),
routes
})
...
export default router
# 配置说明
需在 Nginx 做特殊配置(添加 try_files $uri $uri/ /dist/index.html;
来保证回车或刷新时不出现404)。
这里的/dist/index.html
地址需根据实际Nginx配置的根目录做调整,确保能重定向到前端的index页面即可。
# 3. 前端打包配置
目前新页面使用的脚手架为 Vite,故在vite.config.js
里加上base: "/example/"
,如下(Vue-Cli也可以找到类似的配置)
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: "/example/",
... # 其他配置
})
这里的 base
表示 开发或生产环境服务的公共基础路径,默认值为'/'
,
此处需设为Nginx上配置的唯一匹配路由,主要是保证打包后的资源都能正常访问
以下搬运Vite官网对该配置字段的介绍:
如果你需要在嵌套的公共路径下部署项目,只需指定 base 配置项,然后所有资源的路径都将据此配置重写。这个选项也可以通过命令行参数指定,例如 vite build --base=/my/public/path/。
由 JS 引入的资源 URL,CSS 中的 url() 引用以及 .html 文件中引用的资源在构建过程中都会自动调整,以适配此选项。
当然,情况也有例外,当访问过程中需要使用动态连接的 url 时,可以使用全局注入的 import.meta.env.BASE_URL 变量,它的值为公共基础路径。注意,这个变量在构建时会被静态替换,因此,它必须按 import.meta.env.BASE_URL 的原样出现(例如 import.meta.env['BASE_URL'] 是无效的)