如何配置create-react-app以从子目录提供应用程序服务?

80

我有一个经典的Web应用程序,由服务器渲染。我想在React中创建单页管理面板。我想从https://smyapp.example.com/admin/提供管理面板服务。我尝试使用create-react-app,但它假定我从根URL提供SPA。我应该如何配置create-react-app来使应用程序从"admin"子目录中提供服务?在文档中我找到了"homepage"属性,但如果我正确理解它,它需要完整的URL。我无法提供完整的URL,因为我的应用程序部署在几个环境中。


你能详细说明一下“我不能提供完整的URL,因为我的应用程序部署在几个环境中”吗? - Gabriel Bleu
@GabrielBleu 在文档中,我发现了类似“homepage”:“http://mywebsite.com/subdirectory”的内容,但是我不想在这里提供完整的域名。 - guest
由于路由可以由“router”“basename”处理,我认为这个URL仅适用于捆绑包,也许您可以链接到一个共享的URL,例如“statics.mywebsites.com”? - Gabriel Bleu
10个回答

83

除了你的要求,我添加了我的:

  • 应通过环境变量CD执行。
  • 如果我需要重命名子目录,则只需更改环境变量即可。
  • 它应该与react-router一起工作。
  • 它应该与scss(sass)和html一起工作。
  • 在dev模式(npm start)下一切都应该正常工作。

不久前,我还不得不在Angular2+项目中实现它,我发现在React中比在Angular2+中实现更难,在Angular2+中只需使用ng build --base-href /<project_name>/即可。 来源


简短版本

  1. 在构建之前,将PUBLIC_URL环境变量设置为您的子目录的值,例如/subdir。您也可以将此变量放入.env.production文件中。(如果您没有该文件,请查看文档
  2. public/index.html中添加基本元素如下,这是用于静态文件(如图像)的。
<base href="%PUBLIC_URL%/">
  1. 如果您在 public/index.html 中有自定义的 link 元素,请确保它们以 %PUBLIC_URL% 为前缀(例如,manifest.jsonfavicon.ico 的 href 属性)。
  2. 如果您使用 BrowserRouter,可以添加 basename 属性:
<BrowserRouter basename={process.env.PUBLIC_URL} />
  1. 如果您使用Router,因为您需要访问history.push方法以编程方式更改页面,请执行以下操作:

// history.tsx
import {createBrowserHistory} from 'history';

export default createBrowserHistory({ basename: process.env.PUBLIC_URL });

<!-- Where your router is, for me, App.tsx -->
<Router history={history}>
  ...
</Router>
  1. 在元素内部使用相对链接
<!-- "./assets/quotes.png" is also ok, but "/assets/quotes.png" is not -->
<img src="assets/quotes.png" alt="" />

将你的background-image链接从scss移动到jsx/tsx文件中(如果使用css文件可能不需要这样做):
/*remove that*/
background-image: url('/assets/background-form.jpg');

<section style={{backgroundImage: `url('assets/background-form.jpg')`}}>
...

你应该完成了。


附加信息

我更喜欢在 package.json 中使用 PUBLIC_URL 而不是 homepage,因为我想使用在 GitLab 上设置的环境变量来设置子目录。有关此主题的相关资源:

PUBLIC_URL 会覆盖 homepage,如果提供域名,则 PUBLIC_URL 也会接受它。 如果只设置了 homepage,则 PUBLIC_URL 将被设置为 homepage 的值。


如果您不想在 index.html 中使用 base 元素(我不知道为什么),则需要自己添加 process.env.PUBLIC_URL 到每个链接中。请注意,如果您有带有基础元素的 react-router,但没有设置 basename prop,则会收到警告。


使用错误的相对路径编译 Sass 也将无法编译。由于 ModuleScopePlugin 的限制,即使使用正确的相对路径到您的 ../public/assets 文件夹,它也不会编译。您可以通过将图像移动到 src 文件夹中来避免此限制,我还没有尝试过。


似乎没有办法在开发模式(npm start)下测试相对路径。请参见评论


最后,这些 stackoverflow 链接有相关问题:


1
这应该是被接受的答案。使用环境变量比使用package.json好得多。 - Scaraux
这在我的开发环境中运行良好,但在生产环境中却不行。我有一个奇怪的设置,无法从 / 提供服务,这可能会影响静态/资产文件。 - Powersource
使用 .env 文件是在开发和生产环境中运行应用程序的一个很好的建议,可以避免任何痛苦。 - Arthur Z.
感谢您提供详细的答案,但我并不认为这是一个“简短”的版本! - rodorgas

51

对于 create-react-app v2 和 react-router v4,我使用以下组合来在 "/app" 下提供生产环境(staging、uat 等)应用:

package.json:

"homepage": "/app"

然后在应用程序入口点中:

 <BrowserRouter basename={process.env.PUBLIC_URL}>
  {/* other components */}
 </BrowserRouter>

一切在本地开发和部署环境下都“只是工作”。祝好运!


DRY(不要重复自己)怎么办?在这种情况下,您的公共URL出现在两个地方... - Liga
我还需要在public/index.html中添加<base href="/app/" />,以使资源正常工作 - 资源使用相对URL(例如<link href="css.css" rel="stylesheet" />)。 - kjetilh

31

您需要在package.json中添加条目。

在您的package.json中添加一个键“homepage”:“your-subfolder /”,所有静态文件都将从“your-subfolder”加载

如果没有子文件夹并且您需要从相同文件夹加载,则需要将路径添加为“./”,或者删除具有"homepage":"xxxxxxxxxx"的整行

"homepage": "./"

来自官方文档

默认情况下,Create React App会生成一个构建,假设您的应用程序托管在服务器根目录下。 要覆盖此设置,请在您的package.json中指定主页,例如:

"homepage": "http://mywebsite.com/relativepath",

注意: 如果您正在使用react-router@^4,则可以在任何<Router>上使用basename prop路由<Link>

来自这里,还请查看官方CRA文档


1
花了一整天的时间来部署 CRA 构建,最终通过这个答案找到了解决方案! - roshnet

11

要获取相对URL,您可以像这样构建应用程序:

PUBLIC_URL="." npm run build

2
也许你可以使用 react-router 和它的 相对 basename 参数,这样您就可以从子目录中提供您的应用程序。 basename 是所有位置的基本网址。如果您的应用程序是从服务器上的子目录提供的,则需要将其设置为该子目录。格式正确的 basename 应该有一个前导斜杠,但没有尾随斜杠。
例如:
<BrowserRouter basename="/calendar"/>

因此,<Link to="/today"/>将呈现为<a href="/calendar/today">

参见:https://reacttraining.com/react-router/web/api/BrowserRouter/basename-string


2
在package.json中加入类似以下内容:"homepage" : "http://localhost:3000/subfolder",即可在任何公共或本地服务器上正常工作。当然,subfolder必须是您的文件夹名称。请注意保留html标签。

创建React应用程序时,图像和其他资源是否也将从该文件夹中提供服务? - TKoL
3
这在我的情况下没有发生,我卡在尝试从子目录获取资产的过程中。 - Abhay Kumar

1
在我们的情况下,我们按照Ambroise's answer中描述的一切都做了,但是在生产环境中得到了一个空白页面,在控制台中没有错误或警告。结果发现这是BrowserRouter的问题 - 我们通过设置BrowserRouter的basename来解决了这个问题:
<BrowserRouter basename="/our_subfolder/" />

0

按照以下步骤从子目录中提供React应用程序的服务。

  • package.json文件中添加homepage条目。

    "homepage": "xyz/",
    
  • 如果您正在从子目录提供应用程序,请更改基本路由名称。

    <BrowserRouter basename={"/xyz"}>
        <Routes/>
    </BrowserRouter>
    
  • 如果您正在使用Nginx服务器,则必须指定位置,如下所示:

      location /xyz {
              alias /build-folder-path;
              try_files $uri /$uri /xyz/index.html;
      }
    

注意:xyz是子目录名称。


0
我曾经遇到过类似的问题。需要从Godaddy托管中提供React应用程序。假设上下文路径应为/web,即:http://example_123.com/web 对于React 18.2.0和react-router-dom: 6.11.0,以下更改对我来说都有效:
1. 在package.json中将主页设置为/web,即:homepage: "/web" 2. 将BrowserRouter中的basename更新为process.env.PUBLIC_URL
    <BrowserRouter basename={process.env.PUBLIC_URL}> 
      {/*Your components*/} 
    </BrowserRouter>

3. 构建应用程序并将构建文件放在/public_html/web文件夹中,例如:/public_html/web。
4. 在Godaddy的public_html文件夹中创建index.html,并将其重定向到/web。
<html> <head>

<title>welcome to example_123</title> 

<!--<meta http-equiv="refresh" content="0.2; URL=web/index.html"> -->
<meta name="keywords" content="automatic redirection"> 

</head> 

<body> </body> 

</html>

奖金

如果想将您的内容放在 /web 中,并且不想要上下文路径,请按照以下步骤进行:

  1. 在 package.json 中将主页设置为 /web
  2. 将构建文件放在 public_html 的 /web 中
  3. 只需将 /web 中的 index.html 移动到 public_html 中,即:将构建生成的 index.html 放在 public_html 中,将其他所有文件放在 /web 中

-2

您可以在webpack配置中指定公共路径,同时使用React路由基础路径。

链接到公共路径:https://webpack.js.org/guides/public-path/

请注意,公共路径需要以斜杠 / 开头和结尾才能有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接