Docker容器Nginx部署React应用:无法加载App.tsx问题求助
本地执行npm run dev运行正常的React+Vite+TS项目,通过多阶段Dockerfile构建镜像后,容器运行无报错,但访问localhost时仅能看到public目录下的空白index.html,无法加载TSX编译后的内容。
相关配置文件
Dockerfile
# Step 1: Use an official Node runtime as a parent image FROM node:20.10.0 as build # Step 2: Set the working directory in the container RUN mkdir /app WORKDIR /app # Step 3: Copy the package.json files and install dependencies COPY package-lock.json ./ COPY package.json ./ RUN npm install --verbose RUN npm set audit false # Step 4: Copy the rest of your app's source code COPY . /app # Step 5: Build your app RUN npm run build # Stage 2: Serve the application with Nginx FROM nginx:alpine # Step 6: Copy build artifacts from the 'build' stage to the Nginx directory COPY --from=build /app/build /usr/share/nginx/html/ COPY nginx.conf /etc/nginx/nginx.conf COPY default.conf /etc/nginx/conf.d/default.conf # Step 7: Expose port 80 for Nginx EXPOSE 80 # Step 8: Start Nginx CMD ["nginx", "-g", "daemon off;"]
Nginx配置文件
nginx.conf
events { # Event-driven configuration } http { include /etc/nginx/mime.types; default_type application/octet-stream; # Include server configurations include /etc/nginx/conf.d/*.conf; # Further http configurations }
default.conf
server { # Listen on port 80 for incoming connections listen 80; # Define the root directory where your files are located # This should be the path to your built React application root /usr/share/nginx/html; # Specify the file to serve as index index index.html; # Handle Single Page Application (SPA) routing: # Attempt to serve any requested file or route, fall back to index.html if not found location / { try_files $uri $uri/ /index.html; } # Custom error pages (optional) error_page 500 502 503 504 /50x.html; location = /50x.html { internal; } }
容器运行日志
2024/01/06 13:06:51 [notice] 1#1: start worker process 32 2024/01/06 13:06:51 [notice] 1#1: start worker process 33 2024/01/06 13:06:51 [notice] 1#1: start worker process 34 2024/01/06 13:06:51 [notice] 1#1: start worker process 35 2024/01/06 13:06:51 [notice] 1#1: start worker process 36 2024/01/06 13:06:51 [notice] 1#1: start worker process 37 172.17.0.1 - - [06/Jan/2024:13:07:15 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "-" 172.17.0.1 - - [06/Jan/2024:13:07:19 +0000] "\x16\x03\x01\x02,\x01\x00\x02(\x03\x03\xF3P\xD9\xB0C\xB6\x9D1\x9D\x14\xE3\xBFr\x95\x1B\x22_\xFD8I\xA7\xE2\x16#DNy\x03\x5C\xED\x9F\x19 \xB61\xB2\x89C\xAA\xCA=\x8E\xA2\x18\x8B\x9Cs\x12f\x9C\xBD\xF9\x80J\xD2@eV;\xCA$i\xE8L\xDE\x00 \xFA\xFA\x13\x01\x13\x02\x13\x03\xC0+\xC0/\xC0,\xC00\xCC\xA9\xCC\xA8\xC0\x13\xC0\x14\x00\x9C\x00\x9D\x00/\x005\x01\x00\x01\xBF\xCA\xCA\x00\x00\x00\x0B\x00\x02\x01\x00\x00" 400 157 "-" "-" "-" 172.17.0.1 - - [06/Jan/2024:13:07:19 +0000] "\x16\x03\x01\x02\x00\x01\x00\x01\xFC\x03\x03-:\xE1\x8B/\xAA\xA0k^\xBC \xCF\xBA\x19\xF0\xFC\x18\xE5)r]\x9CZ42\xB3\xB7\xA3\xFB\x95`\x82 yL\xE0\x1DW[\x9ENwp\x97L\xC5\xE8[p6\xA6\xA7N^\xAB\xBE\xE8L\xD3\x0E\x83\x87 \xAD-\x00 jj\x13\x01\x13\x02\x13\x03\xC0+\xC0/\xC0,\xC00\xCC\xA9\xCC\xA8\xC0\x13\xC0\x14\x00\x9C\x00\x9D\x00/\x005\x01\x00\x01\x93\xFA\xFA\x00\x00\x00" 400 157 "-" "-" "-" 172.17.0.1 - - [06/Jan/2024:13:07:19 +0000]
public/index.html内容
<!DOCTYPE html> <html lang="en"> <head> <meta name="monetag" content="5a02f84c329734f784dc0af09b7ae775"> <meta charset="UTF-8" /> <link rel="icon" type="image/png" href="../favicon192.png" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React + TS 2</title> </head> <body> <div id="root"></div> </body> </html>
容器内/usr/share/nginx/html目录文件列表
/usr/share/nginx/html/ls -ltr drwxr-xr-x 2 root root 4096 Jan 6 09:54 source-map-url drwxr-xr-x 19 root root 4096 Jan 6 10:07 @webassemblyjs drwxr-xr-x 24 root root 4096 Jan 6 10:07 @types drwxr-xr-x 4 root root 4096 Jan 6 10:07 enhanced-resolve drwxr-xr-x 3 root root 4096 Jan 6 10:07 loader-runner drwxr-xr-x 2 root root 4096 Jan 6 10:07 infer-owner drwxr-xr-x 4 root root 4096 Jan 6 10:07 webpack-sources drwxr-xr-x 10 root root 4096 Jan 6 10:07 webpack drwxr-xr-x 4 root root 4096 Jan 6 10:07 watchpack drwxr-xr-x 3 root root 4096 Jan 6 10:07 tapable drwxr-xr-x 3 root root 4096 Jan 6 10:07 serialize-javascript drwxr-xr-x 3 root root 4096 Jan 6 10:07 schema-utils -rwxr-xr-x 1 root root 1738 Jan 6 10:12 package.json -rwxr-xr-x 1 root root 903386 Jan 6 10:12 package-lock.json -rwxr-xr-x 1 root root 201 Jan 6 10:37 vite.config.ts -rwxr-xr-x 1 root root 758 Jan 6 11:36 Dockerfile -rwxr-xr-x 1 root root 398 Jan 6 11:50 index.html -rwxr-xr-x 1 root root 4 Jan 6 11:52 COMMIT_EDITMSG -rwxr-xr-x 1 root root 8181 Jan 6 11:52 index -rwxr-xr-x 1 root root 41 Jan 6 11:52 ORIG_HEAD drwxr-xr-x 3 root root 4096 Jan 6 11:55 logs -rwxr-xr-x 1 root root 103 Jan 6 13:01 FETCH_HEAD -rwxr-xr-x 1 root root 168 Jan 6 13:03 nginx.conf /usr/share/nginx/html
核心问题定位
从容器内目录列表可以明显看到:/usr/share/nginx/html下存放的是项目根目录的所有文件(包括node_modules依赖、Dockerfile、源码文件等),而非React项目构建后的静态产物。这说明构建阶段的产物没有正确复制到Nginx的服务目录中。
具体解决步骤
1. 修正Vite构建产物路径
Vite默认执行npm run build后,产物会输出到dist目录,而非Dockerfile中写的build目录。修改Dockerfile中复制构建产物的路径:
# Stage 2中修改复制路径 COPY --from=build /app/dist /usr/share/nginx/html/
如果你的项目自定义了构建输出目录,可查看vite.config.ts中的build.outDir配置,对应修改上述路径。
2. 验证本地构建是否正常
先在本地执行npm run build,检查项目根目录是否生成了dist(或自定义的输出)文件夹,且文件夹内包含index.html和assets目录(存放编译后的JS/CSS资源)。如果构建失败,需先修复TS语法错误、依赖缺失等问题。
3. 清理Docker缓存重新构建
使用无缓存构建命令,避免旧缓存影响:
docker build --no-cache -t react-app .
运行容器后,再次检查/usr/share/nginx/html目录,应只包含构建后的静态文件,而非项目源码。
4. 修正index.html中的资源路径
public/index.html里的favicon路径../favicon192.png是错误的,构建后favicon会被复制到dist根目录,应改为:
<link rel="icon" type="image/png" href="/favicon192.png" />
内容的提问来源于stack exchange,提问作者Hard Worker

