一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
准备
前端开发技术栈使用umi
配置history路由模式使用[nginx](nginx: download)
一、域名下只部署一个前端(spa)应用,并想开启history路由模式
这个配置起来就相对简单
build 前端应用
.umirc.ts
1、将base 设为 '/'
就是将项目部署到域名根路径
2、将routes配置注释掉,使用[约定式路由](约定式路由 (umijs.org))
export default defineConfig({
base: '/',
nodeModulesTransform: {
type: 'none',
},
// routes: [
// { path: '/', component: '@/pages/index' },
// ],
fastRefresh: {},
});
src/pages目录如下 有三个路由,分别是(可以先无视.less文件)
- /
- /b
- /b/c
src
├── a.md
└── pages
├── b
│ ├── c
│ │ ├── index.less
│ │ └── index.tsx
│ ├── index.less
│ └── index.tsx
├── index.less
└── index.tsx
示例图如下:
/
/b
/b/c
npm run build
192:umi-app 11$ npm run build
> @ build /Users/11/testspace/umi-app
> umi build
✔ Webpack
Compiled successfully in 28.96s
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db
Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
DONE Compiled successfully in 28963ms 12:04:05 PM
File Size Gzipped
dist/umi.js 528.2 KB 170.6 KB
dist/umi.css 26.7 KB 4.4 KB
打包成功了!
现在的问题是,如果你不配置history路由模式的话,你部署到服务器上,如果你直接访问/b或/b/c是会报404的 。。。。。。
所以下面讲如何在nginx为前端应用配置history路由模式
接着将打包好的dist里面内容 copy 到nginx>html中
如下所示:
nginx 配置
[完整demo](spa_simple_history_mode: 在前后端分离的模式下,如果你的域名下只布署一个前端应用的话,使用nginx将你的应的路由模式设置为history模式 (gitee.com))
在线预览:loujinliang.cc:8009/
主要是在location / 模块内配置个try_files,让访问所有路由都去找/index.html
server {
listen 8009;
server_name localhost;
location / {
root html;
# 如果匹配到静太资源,根据实际需要,加入更多表态资源后缀
if ($uri ~ \.(js|css|jpe?g,gif,png,webp,svg)) {
#break 是终止 后续指令执行
break;
}
try_files /index.html =404;
# index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
如果你这个域名需要提供web展示,又提供接口调用。假如/api/xxx都是访问接口的。
那么只需要在上面配置中,新增一段配置。
在线预览:loujinliang.cc:8009/api/a
location ~ /api/.*$ {
default_type text/plain;
# 这里暂时 返回所访问的uri,简单做个演示 /api/xxx可以被命中
return 200 $uri;
# 实际场景 需要使用proxy_pass代理真正的后端服务上
# proxy_pass http://xxxxxxx;
}
完整配置:
server {
listen 8009;
server_name localhost;
location / {
root html;
# 如果匹配到静太资源,根据实际需要,加入更多表态资源后缀
if ($uri ~ \.(js|css|jpe?g,gif,png,webp,svg)) {
#break 是终止 后续指令执行
break;
}
try_files /index.html =404;
# index index.html index.htm;
}
location ~ /api/.*$ {
default_type text/plain;
# 这里暂时 返回所访问的uri,简单做个演示 /api/xxx可以被命中
return 200 $uri;
# 实际场景 需要使用proxy_pass代理真正的后端服务上
# proxy_pass http://xxxxxxx;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
现在,你能完整的部署一个web(spa)应用了,并且还能提供api调用。
但是,还不够!
很多情况 都是,我们这个域名下 会部署多个前端应用。
例如:
这样怎么配呢,你要将所有的前端应用 都配置成history路由模式,总不能每上新一个应用,就去改下nginx配置吧(这也太不智能了)。
如果你想知道如何一劳永逸,请接着往下看
二、一个域名下,n个前端应用都配置history路由模式。
[完整demo](瞬步刀式/multi_spa__history_mode - 码云 - 开源中国 (gitee.com))
首先,我们要先做一件事,让程序知道哪个路径下的是我们的前端应用。
定义规则:
暂定 http//:localhost:8009/web/ 存放我们的前端应用。
每个应用都平放于web路径下,如:
应用一:http//:localhost:8009/web/app1
应用二:http//:localhost:8009/web/app2
应用三:http//:localhost:8009/web/app3
app1、app2、app3应用名字其实是可以随意取的,我这里为了演示,起的比较有规律。
第一步:我们现在要build 三个应用。
app1
注意 base、publicPath配置
export default defineConfig({
base: '/web/app1/',
publicPath: '/web/app1/',
nodeModulesTransform: {
type: 'none',
},
// routes: [
// { path: '/', component: '@/pages/index' },
// ],
fastRefresh: {},
});
另外,我在页面中加入了window.routerBase字段的显示,这样可以在页面中清晰的看到,你在访问哪个应用.
这个字段是npm run build 时, 会把配置文件中的base字段插入到html中,所以这里能访问到。如图:
npm run build
build成功之后 将dist里的内容copy到 nginx>html>web>app1目录中
app1 部署成功!
app2
注意 base、publicPath配置
export default defineConfig({
base: '/web/app2/',
publicPath: '/web/app2/',
nodeModulesTransform: {
type: 'none',
},
// routes: [
// { path: '/', component: '@/pages/index' },
// ],
fastRefresh: {},
});
npm run build
build成功之后 将dist里的内容copy到 nginx>html>web>app2目录中
app2 部署成功!
app3
注意 base、publicPath配置
export default defineConfig({
base: '/web/app3/',
publicPath: '/web/app3/',
nodeModulesTransform: {
type: 'none',
},
// routes: [
// { path: '/', component: '@/pages/index' },
// ],
fastRefresh: {},
});
npm run build
build成功之后 将dist里的内容copy到 nginx>html>web>app3目录中
app3 部署成功!
第二步:nginx配置
添加入下配置
# 区配/web/应用名字 + 任意多个字符(可以没有), 这里其中也会包含静态资源的匹配
location ~ /web/([\w]+).*$ {
root html;
#这个子location 是非静态资源的路径 ,其中try_files 中的$1是捕获到的应用名字
location ~ /web/([\w]+)([\/\w]+)*$ {
try_files /web/$1/index.html =404;
}
}
现在完成的配置如下:
server {
listen 8009;
server_name localhost;
location / {
root html;
# 如果匹配到静太资源,根据实际需要,加入更多表态资源后缀
if ($uri ~ \.(js|css|jpe?g,gif,png,webp,svg)) {
#break 是终止 后续指令执行
break;
}
try_files /index.html =404;
# index index.html index.htm;
}
location ~ /api/.*$ {
default_type text/plain;
# 这里暂时 返回所访问的uri,简单做个演示 /api/xxx可以被命中
return 200 $uri;
# 实际场景 需要使用proxy_pass代理真正的后端服务上
# proxy_pass http://xxxxxxx;
}
# 区配/web/应用名字 + 任意多个字符, 这里其中也会包含静态资源的匹配
location ~ /web/([\w]+).*$ {
root html;
location ~ /web/([\w]+)([\/\w]+)*$ {
try_files /web/$1/index.html =404;
}
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
配置成功,重新加载配置文件
nginx -s reload
第三步 访问你的应用
app1 访问(刷新也没问题)
在线预览:loujinliang.cc:8009/web/app1
app2 访问(刷新也没问题)
在线预览:loujinliang.cc:8009/web/app2
app3 访问(刷新也没问题)
在线预览:loujinliang.cc:8009/web/app3
到此~ 以后随着时间的推移,公司又来新活了,这个时候 你需要开发一个新的应用,依然要使用这个域名,此时你就要按照上面的规则,把你的应用 部署到/web/目录下,就自动为你开启history路由模式了,你就是加100个1000个应用,也不用再去改nginx配置。
优化点: 其实上面的配置
# 区配/web/应用名字 + 任意多个字符, 这里其中也会包含静态资源的匹配
location ~ /web/([\w]+).*$ {
root html;
location ~ /web/([\w]+)([\/\w]+)*$ {
try_files /web/$1/index.html =404;
}
}
这里有个嵌套location,其实也不用非得这么写。
注意、注意、注意
也可以写成这样:
location ~ /web/([\w]+)([\/\w]+)*$ {
try_files /web/$1/index.html =404;
}
但如果直接写成这样的话
它只能匹配/web/应用名/路由路径...,
不能匹配到/web/应用名/静态资源文件...
也就是说,/web/应用所引用的静态资源(如果也在本服务器的话)会被 location /这条规则所匹配到,目前来看也没什么问题,怕的是以后有人对location /这条规则改了什么东西,可能对/web下的应用有影响。不如用嵌套location规则全部把控
逻辑就是
- 外面一层是先匹配到路由和静态资源,
- 再在内部
location匹配纯路由进行try_files
如果能本文章能帮你解决困惑,请点个赞支持。
如果没能帮到你,不建议点踩~
写在最后
location的匹配顺序,建议好好阅读
- 先检测匹配项的内容为
非正则表达式修饰语的location,然后再检测匹配项的内容为正则表达式修饰语的localtion。(总结:先检查非正则,再检查正则) - 匹配项的内容为
正则与非正则都匹配的location,按照匹配项的内容为正则匹配的location执行。(总结:正则与非正则者匹配,选择正则执行) - 所有匹配项的内容均为非正则表达式的location,按照匹配项的内容完全匹配的内容长短进行匹配,即匹配内容多的location被执行。(
总结:都为非正则的匹配,按匹配多少执行) - 所有匹配项的内容均为正则表达式的location,按照书写的先后顺序进行匹配,匹配后就执行,不再做后续检测。(
总结:所有匹配项都为正则的,书写在前的被执行)
以上片段内容,来自《nginx应用与运维实战》一书。