记录一下如何使用 Nginx 实现新老框架页面共存并且可相互跳转
一、需求背景
原项目使用 tornado 框架 动态 render 页面返回,现在需要慢慢引入前后端分离模式,考虑在新功能上用 Vue 实现页面,并且使新老页面可以相互跳转。
二、解决方案
给新页面分配唯一路由(比如 /example/,以下配置以该路由举例 ),
以该路由的开头的页面都会被 Nginx 通过路由正则匹配指向新的静态页面地址( Vue 打包之后的页面),
其他路由仍旧保持原来的路由匹配走原来的 tornado框架 动态 render,
新老页面的相互跳转直接用 href='xxx' 实现。
为实现以上方案,需做三步处理:
- 在 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|
|兼容性|可以兼容一些低版本的浏览器|不兼容低版本浏览器|
具体可见 Vue-router hash和history的区别
目前我们采用 history 模式主要是因为原项目是标准的Url格式,此处做统一,配置如下:
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
// 本地开发时因为example路由下没有页面,直接重定向到A页面
{
path: '/example',
redirect: '/example/aaa'
},
{
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)。
一般实际开发场景中,用 hash 模式较为常见,
一是因为兼容性好(history 模式是 Html5 后提出的,故不兼容低版本浏览器),
二是 hash 模式不需要在 Nginx 上特殊配置。
若需要Url整洁或是别的需求,则考虑 history 模式,一切以实际开发需求为准。
3. build配置
目前新页面使用的脚手架为 Vite,故在vite.config.js里加上base: "/example/",如下(Vue-Cli也可以找到类似的配置)
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’] 是无效的)
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!