今天必须手把手教会你配置history路由模式。

1,255 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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文件)

  1. /
  2. /b
  3. /b/c
src
├── a.md
└── pages
    ├── b
    │   ├── c
    │   │   ├── index.less
    │   │   └── index.tsx
    │   ├── index.less
    │   └── index.tsx
    ├── index.less
    └── index.tsx

示例图如下:

/

image.png

/b

image.png

/b/c

image.png

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中

如下所示:

image.png

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调用。

但是,还不够!

很多情况 都是,我们这个域名下 会部署多个前端应用。

例如:

  1. 应用一 loujinliang.cc/app1/xxx/a

  2. 应用二 loujinliang.cc/app2/xxx/b

  3. 应用三 loujinliang.cc/app3/xxx/c

这样怎么配呢,你要将所有的前端应用 都配置成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

app1app2app3应用名字其实是可以随意取的,我这里为了演示,起的比较有规律。

第一步:我们现在要build 三个应用。

app1

注意 base、publicPath配置

export default defineConfig({
  base: '/web/app1/',
  publicPath: '/web/app1/',
  nodeModulesTransform: {
    type: 'none',
  },
  // routes: [
  //   { path: '/', component: '@/pages/index' },
  // ],
  fastRefresh: {},
});

另外,我在页面中加入了window.routerBase字段的显示,这样可以在页面中清晰的看到,你在访问哪个应用. image.png

这个字段是npm run build 时, 会把配置文件中的base字段插入到html中,所以这里能访问到。如图:

image.png

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 image.png

app2 访问(刷新也没问题)

在线预览:loujinliang.cc:8009/web/app2 image.png

app3 访问(刷新也没问题)

在线预览:loujinliang.cc:8009/web/app3 image.png


到此~ 以后随着时间的推移,公司又来新活了,这个时候 你需要开发一个新的应用,依然要使用这个域名,此时你就要按照上面的规则,把你的应用 部署到/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规则全部把控

逻辑就是

  1. 外面一层是先匹配到路由和静态资源
  2. 再在内部location匹配纯路由进行try_files

如果能本文章能帮你解决困惑,请点个赞支持。

如果没能帮到你,不建议点踩~

写在最后

location的匹配顺序,建议好好阅读

  1. 先检测匹配项的内容为非正则表达式修饰语的location,然后再检测匹配项的内容为正则表达式修饰语的localtion。(总结:先检查非正则,再检查正则
  2. 匹配项的内容为正则非正则都匹配的location,按照匹配项的内容为正则匹配的location执行。(总结:正则与非正则者匹配,选择正则执行
  3. 所有匹配项的内容均为非正则表达式的location,按照匹配项的内容完全匹配的内容长短进行匹配,即匹配内容多的location被执行。(总结:都为非正则的匹配,按匹配多少执行
  4. 所有匹配项的内容均为正则表达式的location,按照书写的先后顺序进行匹配,匹配后就执行,不再做后续检测。(总结:所有匹配项都为正则的,书写在前的被执行

以上片段内容,来自《nginx应用与运维实战》一书。