运维记录1——解决在Nginx下部署CRA项目,二级目录不能访问的问题

如果从头开始搭建React项目,create-react-app通常是开发者的首选。毕竟不是谁都有精力去了解WebPack的复杂配置,而CRA将配置隐藏开箱即用的特性必然会受到普遍欢迎。

根目录访问

到了部署阶段,我通常使用nginx作为web容器,将项目部署到一个根目录下访问。如

# nginx配置
server {
  listen 80;
  server_name my.website.com;
  
  ...
  
  location / {
    alias /data/www/react-project/dist;
    index index.html
  }
}

那么只要我们将项目文件放到对应的目录下,重启nginx即可开始访问web页面。

二级目录访问

有时我们有多个web项目,多个项目不可能同时挂在根目录下,所以我们会划分二级目录来分别访问各个web项目。如

问题1:CSS等资源加载失败

此时,如果简单将nginx配置的location改为/project1,则会出现网页无法访问的错误。

# nginx配置
server {
  listen 80;
  server_name my.website.com;
  
  ...
  
  # location / {
  location /project1 {
    alias /data/www/react-project/dist;
    index index.html
  }
}

现象

从dev工具可以看出,html文件有取得,但css、js等资源引用失败。css和js的文件路径都是http://my.website.com/static/...(或css)。

分析

CRA(create-react-app)的项目配置默认是跑在根目录下的。如果查看dist目录下的html会发现,所有的css或js文件的引用路径都是/开头的绝对路径。

解决

将打包路径从绝对路径改为相对路径:

# package.json
{
  ...
  "homepage": ".",   // 添加homepage属性,将路径改为当前目录
  ...
}

重新编译后看到,所有的资源文件路径都改过来了。

问题2:加载成功,网页空白

重新上传到服务器,更新dist目录下的文件,重启nginx后访问网页。

现象

结果发现,网页仍然是空白一片。查看html的渲染结果,发现似乎js并没有执行。

分析

在react-router-dom的例子中,通常使用的是BrowserRouter。这种类型的Router在向服务器发送请求时,如果相对于二级目录的路由去指向对应的页面路由,就会找不到资源,因此也就不会渲染。

解决

BrowserRouter有一个属性叫做basename,就是用于解决此类问题。

...
import { Route, BrowserRouter as Router, Switch, Redirect } from 'react-router-dom';
...
...
  <Router basename='/project1'>
    <Switch>
      <Redirect exact key='index' path='/' to='/home' />
      { routes }
    </Switch>
  </Router>
...

问题3:访问成功,刷新后404

修改以上配置并编译部署,重启nginx后可正常访问网页。但刷新后,网页变为一片空白。

现象

网页显示,在请求页面路由如http://my.website.com/project... 时,该路由的请求状态为404。

分析

还是因为BrowserRouter的问题,之前能正常访问时因为路由中设置了Redirect,所以能访问到根目录并自动跳转到/home。但直接访问则会访问失败。

解决

在nginx配置中加入try_files命令

location /project1 {
    alias /data/www/react-project/dist;
    # index index.html
    try_files $uri /project1/index.html
  }

这样,在请求$uri时如果找不到对应的资源,会fallback回去加载index.html。

问题解决。