Rails 7中React组件本地正常但Heroku生产环境不显示问题排查
问题:Rails 7 + esbuild环境下React组件在Heroku生产环境不加载
问题背景
我有一个Rails 7应用(使用esbuild,无webpacker),包含多个React组件,大部分在本地和Heroku生产环境都能正常工作。但QuizResultsDashboard组件在Heroku上完全不显示,即使简化组件代码后问题依然存在,甚至连初始的console.log输出都没有。
已确认的排查点
- React已正确安装且正常工作,应用中至少10个其他组件在两种环境下均正常运行
- 页面本身无问题,同一视图渲染其他组件时本地和Heroku均正常
- 生产环境资源预编译无错误
- 代码本身无重大问题,本地可正常渲染
代码实现
application.js中的组件初始化代码
// For Quiz Results Dashboard import QuizResultsDashboard from './components/QuizResultsDashboard'; document.addEventListener('DOMContentLoaded', () => { const quizResultsDashboardContainer = document.getElementById('quizResultsDashboard'); if (quizResultsDashboardContainer) { const userId = quizResultsDashboardContainer.getAttribute('data-user-id'); const root = createRoot(quizResultsDashboardContainer); root.render(<QuizResultsDashboard userId={userId} />); } });
视图中的容器代码
<div class="fc-container"> <div id="quizResultsDashboard" data-user-id="<%= current_login.user.id %>" ></div> </div>
调试异常信息
heroku logs -t未输出有效错误信息- 在
application.js中添加的console.log在本地正常输出,但Heroku生产环境完全无输出 - 生产环境打包后的js文件中找不到QuizResultsDashboard的代码
- 已运行
assets:clobber和assets:precompile,并更新了package.json,问题仍未解决
相关代码片段
初始组件代码
import React, { useEffect, useState } from 'react'; import QuizResultsCategoryChart from './QuizResultsCategoryChart'; const QuizResultsDashboard = ({ userId }) => { console.log("QuizResultsDashboard rendered"); const [categoriesData, setCategoriesData] = useState([]); const [measurementsData, setMeasurementsData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetch(`/api/v1/construct_measurements`) .then(response => { if (!response.ok) { console.error('API response not ok:', response.status, response.statusText); throw new Error(`Network response was not ok: ${response.status} ${response.statusText}`); } return response.json(); }) .then(data => { console.log('API response:', JSON.stringify(data, null, 2)); setCategoriesData(data.categories); setMeasurementsData(data.measurements); setLoading(false); }) .catch(error => { console.error('Fetch error:', error); setError(error.message); setLoading(false); }); }, []); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; return ( <div className="quiz-results-dashboard"> {categoriesData.map(category => ( <div key={category} className="fc-content-box mb-8"> <QuizResultsCategoryChart category={category} measurements={measurementsData.filter(m => m.category === category)} /> </div> ))} </div> ); }; export default QuizResultsDashboard;
简化后的组件代码
import React from 'react'; const QuizResultsDashboard = ({ userId }) => { console.log("QuizResultsDashboard rendered", { userId }); return <div>Quiz Results Dashboard for user {userId}</div>; }; export default QuizResultsDashboard;
调试用的application.js代码
// For Quiz Results Dashboard import QuizResultsDashboard from './components/QuizResultsDashboard'; console.log("THE APPLICATION JS IS HAPPENING.") document.addEventListener('DOMContentLoaded', () => { console.log("IT IS HITTING INSIDE THE DOCUMENT LISTENER.") const quizResultsDashboardContainer = document.getElementById('quizResultsDashboard'); console.log(quizResultsDashboardContainer); if (quizResultsDashboardContainer) { const userId = quizResultsDashboardContainer.getAttribute('data-user-id'); const root = createRoot(quizResultsDashboardContainer); root.render(<QuizResultsDashboard userId={userId} />); } }); // For MessageBoard import MessageBoard from './components/MessageBoard'; document.addEventListener('DOMContentLoaded', () => { const messageBoardContainer = document.getElementById('messageBoard'); console.log("WE'RE IN THE MESSAGE BOARD") if (messageBoardContainer) { const userId = messageBoardContainer.getAttribute('data-user-id'); const root = createRoot(messageBoardContainer); root.render(<MessageBoard userId={userId} />); } });
package.json内容
{ "name": "app", "private": true, "dependencies": { "@fortawesome/fontawesome-free": "^6.5.2", "@hotwired/stimulus": "^3.2.2", "@hotwired/turbo-rails": "^8.0.4", "@rails/actioncable": "^7.1.3-2", "@tailwindcss/forms": "^0.5.7", "autoprefixer": "^10.4.19", "axios": "^1.6.8", "chart.js": "^4.4.3", "esbuild": "^0.20.2", "esbuild-plugin-jsx": "^1.0.1", "lodash": "^4.17.21", "postcss": "^8.4.38", "react": "^18.2.0", "react-actioncable-provider": "^2.0.0", "react-chartjs-2": "^5.2.0", "react-datepicker": "^7.3.0", "react-dom": "^18.3.1", "react-infinite-scroll-component": "^6.1.0", "react-intersection-observer": "^9.10.2", "react-markdown": "^9.0.1", "react-window": "^1.8.10", "tailwindcss": "^3.4.3" }, "scripts": { "build": "esbuild app/javascript/*.* app/javascript/**/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets --loader:.js=jsx --loader:.ttf=file --loader:.woff2=file --external:@rails/actioncable", "build:css": "tailwindcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css --minify" }, "devDependencies": { "@babel/preset-react": "^7.24.1", "esbuild-plugin-copy": "^2.1.1" } }
排查方向建议
- 检查esbuild打包路径匹配:当前build命令使用
app/javascript/*.* app/javascript/**/*.*,确认组件文件路径是否被正确覆盖,比如组件是否在app/javascript/components/目录下,同时注意文件名大小写(Heroku基于Linux,区分大小写,本地Mac可能不区分) - 本地模拟生产打包验证:执行
npm run build生成生产环境打包文件,检查app/assets/builds/application.js中是否包含QuizResultsDashboard的代码及调试用console.log,如果本地打包也缺失,说明是打包配置问题 - 替换DOMContentLoaded事件:由于使用了Turbo Rails,
DOMContentLoaded事件可能不会触发,替换为turbo:load事件,或同时监听两个事件:function initQuizResultsDashboard() { const quizResultsDashboardContainer = document.getElementById('quizResultsDashboard'); if (quizResultsDashboardContainer) { const userId = quizResultsDashboardContainer.getAttribute('data-user-id'); const root = createRoot(quizResultsDashboardContainer); root.render(<QuizResultsDashboard userId={userId} />); } } document.addEventListener('turbo:load', initQuizResultsDashboard); document.addEventListener('DOMContentLoaded', initQuizResultsDashboard); - 检查Heroku构建日志:执行
heroku logs --source build查看构建过程,确认npm run build是否被正确执行,有没有打包报错或步骤遗漏 - 确认组件导入路径:检查
import QuizResultsDashboard from './components/QuizResultsDashboard';的路径是否正确,若组件后缀为.jsx,确认esbuild的--loader:.js=jsx配置是否能正确处理,或尝试在导入时添加后缀 - 验证生产环境Asset配置:确认Rails生产环境配置中
config.assets.compile是否为true,避免静态资源未被正确编译 - 清除浏览器缓存:在Heroku页面强制刷新(Ctrl+Shift+R),查看network标签下的
application.js是否加载最新版本,排除缓存问题
内容的提问来源于stack exchange,提问作者Liz
相关产品推荐
相关产品推荐

