我是Heroku和部署的新手。我使用create-react-app创建了一个项目,并使用redux作为框架。部署到Heroku时,我遇到了反应路由器的问题。当我点击我的应用程序中的链接时,路由器可以工作,但是当我刷新页面时,它会抛出404 Not Found错误。
这是我的index.js
import { Router, Route, browserHistory, IndexRoute } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import { Provider } from 'react-redux'
import ReactDOM from 'react-dom'
import React from 'react'
import App from './containers/App'
import configure from './store'
import Dashboard from './components/v1/dashboard/dashboard';
import Login from './components/v1/login/login';
const store = configure();
const history = syncHistoryWithStore(browserHistory, store);
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Login} />
<Router path="/dashboard" component={Dashboard}/>
</Route>
</Router>
</Provider>,
document.getElementById('root')
)
这是我的webpack.config.prod.js
'use strict';
var autoprefixer = require('autoprefixer');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var ManifestPlugin = require('webpack-manifest-plugin');
var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
var paths = require('./paths');
var getClientEnvironment = require('./env');
var publicPath = paths.servedPath;
var shouldUseRelativeAssetPaths = publicPath === './';
var publicUrl = publicPath.slice(0, -1);
var env = getClientEnvironment(publicUrl);
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.');
}
const cssFilename = 'static/css/[name].[contenthash:8].css';
const extractTextPluginOptions = shouldUseRelativeAssetPaths
? { publicPath: Array(cssFilename.split('/').length).join('../') }
: undefined;
module.exports = {
bail: true,
devtool: 'source-map',
entry: [
require.resolve('./polyfills'),
paths.appIndexJs
],
output: {
path: paths.appBuild,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: publicPath
},
resolve: {
fallback: paths.nodePaths,
extensions: ['.js', '.json', '.jsx', ''],
alias: {
'react-native': 'react-native-web'
}
},
module: {
preLoaders: [
{
test: /\.(js|jsx)$/,
loader: 'eslint',
include: paths.appSrc
}
],
loaders: [
{
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.css$/,
/\.scss$/,
/\.json$/,
/\.(jpe?g|png|gif|svg)$/i
],
loader: 'url',
query: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
},
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: 'babel',
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(
'style',
'css?importLoaders=1&modules&localIdentName=[name]__[local]___[hash:base64:5]!postcss',
extractTextPluginOptions
)
},
{
test: /\.scss$/,
loaders: ["style", "css", "sass"]
},
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loader: 'file',
query: {
name: 'static/media/[name].[hash:8].[ext]'
}
}
]
},
postcss: function() {
return [
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
]
}),
];
},
plugins: [
new InterpolateHtmlPlugin(env.raw),
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
}),
new webpack.DefinePlugin(env.stringified),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
screw_ie8: true, // React doesn't support IE8
warnings: false
},
mangle: {
screw_ie8: true
},
output: {
comments: false,
screw_ie8: true
}
}),
new ExtractTextPlugin(cssFilename),
new ManifestPlugin({
fileName: 'asset-manifest.json'
})
],
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
这是我的package.json
{
"name": "olep2",
"version": "0.1.0",
"private": true,
"dependencies": {
"draft-js": "^0.10.0",
"draft-js-export-html": "^0.5.2",
"es6-promise": "^4.0.5",
"express": "^4.15.2",
"jquery": "^3.1.1",
"lodash": "^4.17.4",
"moment": "^2.17.1",
"react": "^15.5.4",
"react-bootstrap": "^0.30.7",
"react-day-picker": "^5.0.0",
"react-dom": "^15.5.4",
"react-fontawesome": "^1.5.0",
"redux-thunk": "^2.2.0",
"requirejs": "^2.3.2"
},
"devDependencies": {
"classnames": "^2.2.3",
"cross-env": "^4.0.0",
"css-loader": "0.26.1",
"es6-promise": "^4.0.5",
"file-loader": "0.10.0",
"image-webpack-loader": "^3.2.0",
"node-sass": "^4.5.0",
"npm-run-all": "^4.0.2",
"postcss-loader": "1.2.2",
"prop-types": "^15.5.4",
"react-hot-loader": "^1.3.0",
"react-redux": "^4.4.0",
"react-router": "^2.0.0",
"react-router-redux": "^4.0.0",
"redux": "^3.3.1",
"redux-actions": "^0.9.1",
"rucksack-css": "^0.8.5",
"sass-loader": "^6.0.2",
"style-loader": "0.13.1",
"autoprefixer": "6.7.2",
"babel-core": "6.22.1",
"babel-eslint": "7.1.1",
"babel-jest": "18.0.0",
"babel-loader": "6.2.10",
"babel-preset-react-app": "^2.2.0",
"babel-runtime": "^6.20.0",
"case-sensitive-paths-webpack-plugin": "1.1.4",
"chalk": "1.1.3",
"connect-history-api-fallback": "1.3.0",
"cross-spawn": "4.0.2",
"detect-port": "1.1.0",
"dotenv": "2.0.0",
"eslint": "3.16.1",
"eslint-config-react-app": "^0.6.2",
"eslint-loader": "1.6.0",
"eslint-plugin-flowtype": "2.21.0",
"eslint-plugin-import": "2.0.1",
"eslint-plugin-jsx-a11y": "4.0.0",
"eslint-plugin-react": "6.4.1",
"extract-text-webpack-plugin": "1.0.1",
"fs-extra": "0.30.0",
"html-webpack-plugin": "2.24.0",
"http-proxy-middleware": "0.17.3",
"jest": "18.1.0",
"json-loader": "0.5.4",
"object-assign": "4.1.1",
"promise": "7.1.1",
"react-dev-utils": "^0.5.2",
"url-loader": "0.5.7",
"webpack": "1.14.0",
"webpack-dev-server": "1.16.2",
"webpack-manifest-plugin": "1.1.0",
"whatwg-fetch": "2.0.2"
},
"scripts": {
"start": "node scripts/start.js ",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom"
},
"jest": {
"collectCoverageFrom": [
"src/**/*.{js,jsx}"
],
"setupFiles": [
"<rootDir>/config/polyfills.js"
],
"testPathIgnorePatterns": [
"<rootDir>[/\\\\](build|docs|node_modules|scripts)[/\\\\]"
],
"testEnvironment": "node",
"testURL": "http://localhost",
"transform": {
"^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
},
"transformIgnorePatterns": [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"
],
"moduleNameMapper": {
"^react-native$": "react-native-web"
}
},
"babel": {
"presets": [
"react-app"
]
},
"eslintConfig": {
"extends": "react-app"
}
}
答案 0 :(得分:2)
问题是您的网络服务器仅为根路由(“/”)提供“index.html”
您必须以所有路由(或仅使用的路由)将为index.hml提供服务的方式配置Web服务器
阅读以获取更多信息 Heroku Buildpack for create-react-app: static hosting for React.js web apps
答案 1 :(得分:0)
Server.js:
app.get("*", (req, res) => {
let url = path.join(__dirname, '../client/build', 'index.html');
if (!url.startsWith('/app/')) // we're on local windows
url = url.substring(1);
res.sendFile(url);
});
为我工作。