basename 和 homepage 未按预期工作以在开发中提供 /static/ 文件
basename and homepage not working as expected to serve /static/ files in development
不确定这是 Kubernetes、ingress-nginx
还是 ReactJS (create-react-app
) 问题...
项目结构
new-app/
client/
src/
App.js
index.js
Test.js
package.json
k8s/
client.yaml
ingress.yaml
server/
skaffold.yaml
问题
- 当使用 Skaffold
启动集群时,ReactJS 前端应 运行 在 192.168.64.5/client
- 导航到
192.168.64.5/client
和空白屏幕
- 开发者控制台显示:
基本上,它试图提供来自 /static
的静态文件,但我需要它们来自 /client/static
建议的解决方案
假设这是一个 ReactJS 问题,解决方案如下:
- 在
package.json
中设置 homepage
- 在
react-router-dom
中设置 basename
- 正在关注 this "elegant" solution
None 似乎适用于我的情况。仍然显示尝试从 /static
而不是 /client/static
.
提供的资产
ReactJS 应用代码
// App.js
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import './App.css';
import Test from './Test';
const App = () => {
return (
<BrowserRouter
basename='/client'>
<>
<Route exact path='/' component={Test} />
</>
</BrowserRouter>
);
}
export default App;
// Test.js
import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
const Test = () => {
const [data, setData] = useState('');
useEffect(() => {
const fetchData = async () => {
const result = await axios('/api/auth/test/');
setData(result.data);
};
fetchData();
}, []);
return (
<div className='App'>
<header className='App-header'>
<img src={logo} className='App-logo' alt='logo' />
<p>
Edit <code>src/App.js</code> and save to reload!
</p>
<p>
{data}
</p>
<a
className='App-link'
href='https://reactjs.org'
target='_blank'
rel='noopener noreferrer'
>
Learn React
</a>
</header>
</div>
)
};
export default Test;
{
"name": "client",
"version": "0.1.0",
"private": true,
"homepage": "/client",
"dependencies": {
"axios": "^0.19.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Kubernetes/Skaffold 和 Docker 清单
# Dockerfile.dev
FROM node:alpine
EXPOSE 3000
WORKDIR '/app'
COPY ./client/package.json ./
RUN npm install
COPY ./client .
CMD ["npm", "run", "start"]
# ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/add-base-url: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /client/?(.*)
backend:
serviceName: client-cluster-ip-service
servicePort: 3000
# client.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-deployment
spec:
replicas: 3
selector:
matchLabels:
component: client
template:
metadata:
labels:
component: client
spec:
containers:
- name: client
image: clientappcontainers.azurecr.io/client
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: client-cluster-ip-service
spec:
type: ClusterIP
selector:
component: client
ports:
- port: 3000
targetPort: 3000
# skaffold.yaml
apiVersion: skaffold/v1beta15
kind: Config
build:
local:
push: false
artifacts:
- image: clientappcontainers.azurecr.io/client
docker:
dockerfile: ./client/Dockerfile.dev
sync:
manual:
- src: "***/*.js"
dest: .
- src: "***/*.html"
dest: .
- src: "***/*.css"
dest: .
deploy:
kubectl:
manifests:
- manifests/ingress.yaml
- manifests/client.yaml
那么我做错了什么?
编辑:
我应该注意到,这样做时一切正常:
- path: /?(.*)
backend:
serviceName: client-cluster-ip-service
servicePort: 3000
回购演示问题:
Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in package.json (homepage). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the URL you provide (hostname included). This may be particularly useful when using a CDN to host your application.
解决这个问题的方法有以下三种:
- 将你的 basename 添加到 webpack 输出 -> publicPath(如果你 rejected 你的 create-react-app 项目并且你已经访问 webpack 配置文件)
- 将 PUBLIC_URL="/client" 添加到 .env.production 文件
- 将 主页 属性 添加到 package.json 文件中,如下所示:
{
...
homepage: "/client"
...
}
参考:
https://create-react-app.dev/docs/advanced-configuration/
显然,目前在开发环境中的 CRA 中这是不可能的:
https://github.com/facebook/create-react-app/issues/8222#issuecomment-568308139
目前,要在生产环境中使用不同的主页,使用 CRA(或 craco),您需要设置
# .env.production
PUBLIC_URL="/client"
在文件 .env.production 中(或者 .env.development 如果开发中的根路径不同)然后更改路由器的基本名称:
<BrowserRouter basename={process.env.PUBLIC_URL}>
...
</BrowserRouter>
不确定这是 Kubernetes、ingress-nginx
还是 ReactJS (create-react-app
) 问题...
项目结构
new-app/
client/
src/
App.js
index.js
Test.js
package.json
k8s/
client.yaml
ingress.yaml
server/
skaffold.yaml
问题
- 当使用 Skaffold 启动集群时,ReactJS 前端应 运行 在
- 导航到
192.168.64.5/client
和空白屏幕 - 开发者控制台显示:
192.168.64.5/client
基本上,它试图提供来自 /static
的静态文件,但我需要它们来自 /client/static
建议的解决方案
假设这是一个 ReactJS 问题,解决方案如下:
- 在
package.json
中设置 - 在
react-router-dom
中设置 - 正在关注 this "elegant" solution
homepage
basename
None 似乎适用于我的情况。仍然显示尝试从 /static
而不是 /client/static
.
ReactJS 应用代码
// App.js
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import './App.css';
import Test from './Test';
const App = () => {
return (
<BrowserRouter
basename='/client'>
<>
<Route exact path='/' component={Test} />
</>
</BrowserRouter>
);
}
export default App;
// Test.js
import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
const Test = () => {
const [data, setData] = useState('');
useEffect(() => {
const fetchData = async () => {
const result = await axios('/api/auth/test/');
setData(result.data);
};
fetchData();
}, []);
return (
<div className='App'>
<header className='App-header'>
<img src={logo} className='App-logo' alt='logo' />
<p>
Edit <code>src/App.js</code> and save to reload!
</p>
<p>
{data}
</p>
<a
className='App-link'
href='https://reactjs.org'
target='_blank'
rel='noopener noreferrer'
>
Learn React
</a>
</header>
</div>
)
};
export default Test;
{
"name": "client",
"version": "0.1.0",
"private": true,
"homepage": "/client",
"dependencies": {
"axios": "^0.19.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Kubernetes/Skaffold 和 Docker 清单
# Dockerfile.dev
FROM node:alpine
EXPOSE 3000
WORKDIR '/app'
COPY ./client/package.json ./
RUN npm install
COPY ./client .
CMD ["npm", "run", "start"]
# ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/add-base-url: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /client/?(.*)
backend:
serviceName: client-cluster-ip-service
servicePort: 3000
# client.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-deployment
spec:
replicas: 3
selector:
matchLabels:
component: client
template:
metadata:
labels:
component: client
spec:
containers:
- name: client
image: clientappcontainers.azurecr.io/client
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: client-cluster-ip-service
spec:
type: ClusterIP
selector:
component: client
ports:
- port: 3000
targetPort: 3000
# skaffold.yaml
apiVersion: skaffold/v1beta15
kind: Config
build:
local:
push: false
artifacts:
- image: clientappcontainers.azurecr.io/client
docker:
dockerfile: ./client/Dockerfile.dev
sync:
manual:
- src: "***/*.js"
dest: .
- src: "***/*.html"
dest: .
- src: "***/*.css"
dest: .
deploy:
kubectl:
manifests:
- manifests/ingress.yaml
- manifests/client.yaml
那么我做错了什么?
编辑:
我应该注意到,这样做时一切正常:
- path: /?(.*)
backend:
serviceName: client-cluster-ip-service
servicePort: 3000
回购演示问题:
Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in package.json (homepage). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the URL you provide (hostname included). This may be particularly useful when using a CDN to host your application.
解决这个问题的方法有以下三种:
- 将你的 basename 添加到 webpack 输出 -> publicPath(如果你 rejected 你的 create-react-app 项目并且你已经访问 webpack 配置文件)
- 将 PUBLIC_URL="/client" 添加到 .env.production 文件
- 将 主页 属性 添加到 package.json 文件中,如下所示:
{
...
homepage: "/client"
...
}
参考:
https://create-react-app.dev/docs/advanced-configuration/
显然,目前在开发环境中的 CRA 中这是不可能的:
https://github.com/facebook/create-react-app/issues/8222#issuecomment-568308139
目前,要在生产环境中使用不同的主页,使用 CRA(或 craco),您需要设置
# .env.production
PUBLIC_URL="/client"
在文件 .env.production 中(或者 .env.development 如果开发中的根路径不同)然后更改路由器的基本名称:
<BrowserRouter basename={process.env.PUBLIC_URL}>
...
</BrowserRouter>