Autumn_Ning_Blog icon indicating copy to clipboard operation
Autumn_Ning_Blog copied to clipboard

基于ReactRouter的实现SPA应用的Nginx配置

Open wangning0 opened this issue 7 years ago • 0 comments

先说一下背景,最近再发一个自己的SPA应用的时候,使用了React系全家桶,路由使用了ReactRouterV4.0版本,使用了history模式。在本地开发过程中,没有什么问题,刷新什么的非常好使。

但是当该项目开发的进入尾声的时候,将其部署到服务器,因为都是静态资源,所以就直接用nginx做一下资源代理。而且还有一个使用nginx做反代的原因是因为前后端分离,服务端和前端应用部署不在同一个机器上,需要对API进行反代,当然nginx还有其他非常强大的功能,就不一一列举了。

于是,很快,我就把nginx的配置配好了,如下:

server {
    listen       8099;
    root  /home/ubuntu/r-web/build/;

    charset utf-8;
    access_log  /var/log/fe.log;

    location /api/v1/  {
        proxy_pass http://demo.server.com/api/v1/;
    }
  }

大概的意思就是 应用在8099端口号下,静态资源位于 /home/ubuntu/r-web/build/下,会主动去找该文件夹下的index.html文件,log文件会放在/var/log/fe.log文件下,对于/api/v1/下的api路由会被转发到 http://demo.server.com/api/v1/下。更多的配置可以大家参考nginx配置的文章。

乍一眼看过来是没有问题的,我们访问对应的ip:port也是OK的,但是当我们进入应用的某一个路由时,刷新一下,边看到浏览器的“丑恶嘴脸”

15171540836242

发现这个的时候真的是 “天啊,重大的问题啊!”,但是,发现问题,解决问题,是我们的天性,冷静下来,思考,为什么本地是OK的,用nginx配置后就失败了呢?

查阅一番后,发现是因为当该url请求到来时,nginx会去配置里去找对应的匹配,因为天才不管它也不知道,路由是归谁管的,它是一个很严谨的worker,来了请求他就会去对应的去找该请求,没有找到就返回默认的404页面(默认你没有自己配置),知道原理后我们就去fix这个bug了

首先想到的是将history模式->hash模式,因为路由pathname都是相同的,只是根据hash的不同,交由前端来判断该逻辑,所以可以完美的解决该问题。但是!!考虑到改动的成本非常的大,以及“美观性”,所以就放弃了该方案。

第二个方案就是想着把所有的关HTML有关的路由,通过修改nginx配置全部让他返回index.html,于是就利用了nginx的try_files特性,修改配置如下:

server {
    listen       8099;
    root  /home/ubuntu/r-web/build/;

    charset utf-8;
    access_log  /var/log/fe.log;

    location /api/v1/  {
        proxy_pass http://demo.server.com/api/v1/;
    }

    location ^~ /index {
        try_files $uri /index.html;
    }

    location ^~ /create {
        try_files $uri /index.html;
    }

    location ^~ /help {
        try_files $uri /index.html;
    }

    location ^~ /my_center {
        try_files $uri /index.html;
    }
    location ^~ /login {
        try_files &uri /index.html;
    }
    location ^~ /register {
        try_files &uri /index.html;
    }
    location ^~ /resetPassword {
        try_files &uri /index.html;
    }
    # ...
}

但是这个看起来怪怪的呀,如果业务上再加几个路由,岂不是又要加上一些配置了,如果忘记加了,岂不是很尴尬,于是想起了使用的ReactRouter的一个很好的属性 basename, 于是乎 我们的nginx配置就变成了

server {
    listen       8099;
    root  /home/ubuntu/r-web/build/;

    charset utf-8;
    access_log  /var/log/fe.log;

    location /api/v1/  {
        proxy_pass http://demo.server.com/api/v1/;
    }

    location ^~ /basename/ {
        try_files $uri /index.html;
    }
    # ...
}

从此,妈妈再也不用担心我会忘记加路由还要配置nginx啦!!

在本文的最后,给出两点tips:

  • 当前后端服务不在一块,API的路由配置最好有一个公共的前缀,加上一些api的version组合起来,便于后期统一做转发,以及api的version控制
  • 前端router的设计,特别是SPA应用,最好也能够根据服务也好,模块也好,能够有一个路由的层级关系,无论是维护性、观察性还是扩展性都是一个不错的设计。

谢谢大家,如有错误,恳请斧正。

wangning0 avatar Jan 28 '18 16:01 wangning0