javascript - example - react-router-dom
Las URL del enrutador de reacción no funcionan al actualizar o escribir manualmente (30)
Estoy usando React-router y funciona bien mientras hago clic en los botones de enlace, pero cuando actualizo mi página web no carga lo que quiero.
Por ejemplo, estoy en
localhost/joblist
y todo está bien porque llegué aquí presionando un enlace.
Pero si actualizo la página web obtengo:
Cannot GET /joblist
Por defecto, no funcionó así.
Inicialmente tenía mi URL como
localhost/#/
y
localhost/#/joblist
y funcionaron perfectamente bien.
Pero no me gusta este tipo de URL, así que tratando de borrar ese
#
, escribí:
Router.run(routes, Router.HistoryLocation, function (Handler) {
React.render(<Handler/>, document.body);
});
Este problema no ocurre con
localhost/
, este siempre devuelve lo que quiero.
EDITAR:
Esta aplicación es de una sola página, por lo que
/joblist
no necesita preguntar nada a ningún servidor.
EDIT2: todo mi enrutador.
var routes = (
<Route name="app" path="/" handler={App}>
<Route name="joblist" path="/joblist" handler={JobList}/>
<DefaultRoute handler={Dashboard}/>
<NotFoundRoute handler={NotFound}/>
</Route>
);
Router.run(routes, Router.HistoryLocation, function (Handler) {
React.render(<Handler/>, document.body);
});
Agregar más información a la respuesta de Joshua Dyck .
Si está utilizando Firebase y desea utilizar tanto la ruta raíz como una ruta de subdirectorio, debe agregar el siguiente código en su
firebase.json
:
{
"hosting": {
"rewrites": [
{
"source": "*",
"destination": "/index.html"
},
{
"source": "/subdirectory/**",
"destination": "/subdirectory/index.html"
}
]
}
}
Ejemplo:
Estás construyendo un sitio web para un cliente. Desea que el propietario del sitio web agregue información en https://your.domain.com/management mientras que los usuarios del sitio web navegarán a https://your.domain.com .
En este caso, su
firebase.json
archivo se verá así:
{
"hosting": {
"rewrites": [
{
"source": "*",
"destination": "/index.html"
},
{
"source": "/management/**",
"destination": "/management/index.html"
}
]
}
}
Arreglar el error "no se puede OBTENER / URL" al actualizar o al llamar a la URL directamente.
Configure su webpack.config.js para esperar el enlace dado en rutas como esta.
package.json
"dependencies": {
"babel-polyfill": "^6.23.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"prop-types": "^15.5.6",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-persist": "^4.6.0",
"redux-thunk": "^2.2.0",
"webpack": "^2.4.1"
}
Como estoy usando .Net Core MVC, algo como esto me ayudó:
` expressApp.get(''/*'', (request, response) => {
response.sendFile(path.join(__dirname, ''../public/index.html''));
});`
Básicamente en el lado de MVC, todas las rutas que no coinciden se ajustarán a las
Home/Index
especificadas
startup.cs
.
En el interior
Index
, es posible obtener la url de solicitud original y pasarla donde sea necesario.
startup.cs
public class HomeController : Controller
{
public IActionResult Index()
{
var url = Request.Path + Request.QueryString;
return App(url);
}
[Route("App")]
public IActionResult App(string url)
{
return View("/wwwroot/app/build/index.html");
}
}
Me gusta esta forma de manejarlo. Intente agregar: yourSPAPageRoute / * en el lado del servidor para deshacerse de este problema.
Seguí este enfoque porque incluso la API nativa de Historial HTML5 no admite la redirección correcta en la actualización de la página (que yo sepa).
Nota: La respuesta seleccionada ya ha abordado esto, pero estoy tratando de ser más específico.
Probado y solo quería compartir esto.
Espero eso ayude.
Para aquellos que usan IIS 10, esto es lo que debe hacer para hacer esto bien. Asegúrese de estar usando browserHistory con esto. En cuanto a la referencia, daré el código para el enrutamiento, pero esto no es lo que importa, lo que importa es el siguiente paso después del código del componente a continuación:
module.exports = {
entry: ''./app/index.js'',
output: {
path: path.join(__dirname, ''/bundle''),
filename: ''index_bundle.js'',
publicPath: ''/''
},
Dado que el problema es que IIS recibe una solicitud de los navegadores del cliente, interpretará la URL como si estuviera pidiendo una página, luego devolverá una página 404 ya que no hay una página disponible. Haz lo siguiente:
- Abrir IIS
- Expanda el servidor y luego abra la carpeta Sitios
- Haga clic en el sitio web / aplicación
- Ir a las páginas de error
- Abra el elemento de estado de error 404 en la lista
- En lugar de la opción "Insertar contenido del archivo estático en la respuesta de error", cámbielo a "Ejecutar una URL en este sitio" y agregue el valor de barra "/" a la URL.
Y ahora funcionará bien.
Espero que ayude. :-)
Si está alojando en IIS; Agregar esto a mi configuración web resolvió mi problema
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
Puede hacer una configuración similar para cualquier otro servidor
Si está utilizando Express o algún otro marco en el back-end, puede agregar la configuración similar a la siguiente y verificar la ruta pública de Webpack en la configuración, debería funcionar bien incluso en la recarga si está utilizando BrowserRouter
<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
<remove statusCode="500" subStatusCode="100" />
<remove statusCode="500" subStatusCode="-1" />
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/" responseMode="ExecuteURL" />
<error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
<error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />
</httpErrors>
Si está utilizando Firebase, todo lo que tiene que hacer es asegurarse de tener una propiedad de reescritura en su archivo firebase.json en la raíz de su aplicación (en la sección de alojamiento).
Por ejemplo:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index/.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]
</IfModule>
Espero que esto le ahorre a alguien más un montón de frustración y pérdida de tiempo.
Feliz codificación ...
Lecturas adicionales sobre el tema:
https://firebase.google.com/docs/hosting/full-config#rewrites
Suponga que tiene la siguiente definición de ruta de inicio
<Route exact path="/" render={routeProps => (
<Home routeProps={routeProps}/>
)}/>
{/*optional catch-all router */}
<Route render={routeProps => (
<div><h4>404 not found</h4></div>
)}/>
En su componente Home, puede interceptar la solicitud en el evento ComponentWillMount,
const searchPath = this.props.routeProps.location.search;
if (searchPath){
this.props.routeProps.history.push("/" + searchPath.replace("?",""));
}
else{
/*.... originally Home event */
}
Ahora, en lugar de llamar a / joblist en url, puede solicitar /? Joblist, y el Componente redirigirá automáticamente la solicitud a / joblist (tome nota del signo de interrogación adicional en la ruta)
Tuve el mismo problema y this solución funcionó para nosotros.
Antecedentes:
Estamos alojando múltiples aplicaciones en el mismo servidor. Cuando actualizaríamos el servidor no entendería dónde buscar nuestro índice en la carpeta dist para esa aplicación en particular. El enlace anterior lo llevará a lo que funcionó para nosotros ... Espero que esto ayude, ya que hemos dedicado muchas horas a encontrar una solución para nuestras necesidades.
Estamos usando:
webpack.config.js
/* eslint-disable */
const path = require(''path'');
const webpack = require(''webpack'');
const HtmlWebpackPlugin = require(''html-webpack-plugin'');
const babelPolyfill = require(''babel-polyfill'');
const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
template: __dirname + ''/app/views/index.html'',
filename: ''index.html'',
inject: ''body''
});
module.exports = {
entry: [
''babel-polyfill'', ''./app/index.js''
],
output: {
path: __dirname + ''/dist/your_app_name_here'',
filename: ''index_bundle.js''
},
module: {
rules: [{
test: //.js$/,
loader: ''babel-loader'',
query : {
presets : ["env", "react", "stage-1"]
},
exclude: /node_modules/
}]
},
plugins: [HTMLWebpackPluginConfig]
}
my webpack.config.js
index.js
import React from ''react''
import ReactDOM from ''react-dom''
import Routes from ''./Routes''
import { Provider } from ''react-redux''
import { createHistory } from ''history''
import { useRouterHistory } from ''react-router''
import configureStore from ''./store/configureStore''
import { syncHistoryWithStore } from ''react-router-redux''
import { persistStore } from ''redux-persist''
const store = configureStore();
const browserHistory = useRouterHistory(createHistory) ({
basename: ''/your_app_name_here''
})
const history = syncHistoryWithStore(browserHistory, store)
persistStore(store, {blacklist: [''routing'']}, () => {
console.log(''rehydration complete'')
})
// persistStore(store).purge()
ReactDOM.render(
<Provider store={store}>
<div>
<Routes history={history} />
</div>
</Provider>,
document.getElementById(''mount'')
)
my index.js
var express = require(''express'');
var app = express();
app.use(express.static(__dirname + ''/dist''));
// app.use(express.static(__dirname + ''/app/assets''));
app.set(''views'', __dirname + ''/dist/your_app_name_here'');
app.engine(''html'', require(''ejs'').renderFile);
app.set(''view engine'', ''html'');
app.get(''/*'', function (req, res) {
res.render(''index'');
});
app.listen(8081, function () {
console.log(''MD listening on port 8081!'');
});
my app.js
class App extends Component {
render() {
return (
<Router history={browserHistory}>
<div>
<Root>
<Switch>
<Route exact path={"/"} component={Home} />
<Route path={"/home"} component={Home} />
<Route path={"/createnewproject"} component={CreateNewProject} />
<Route path={"/projects"} component={Projects} />
<Route path="*" component={NotFoundRoute} />
</Switch>
</Root>
</div>
</Router>
)
}
}
render (<App />, window.document.getElementById("app"));
Agregue esto a
webpack.congif.js
// app.js
import { BrowserRouter as Router } from ''react-router-dom''
const App = () {
render() {
return (
<Router>
// your routes here
</Router>
)
}
}
El Webpack Dev Server tiene una opción para habilitar esto.
Abra
package.json
y agregue
--history-api-fallback
.
Estas soluciones me funcionaron.
En su
head
index.html, agregue lo siguiente:
<base href="/">
<!-- This must come before the css and javascripts -->
Luego, cuando se ejecuta con el servidor webpack dev, use este comando.
webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback
--history-api-fallback
es la parte importante
Encontré la solución para mi SPA con react router (Apache). Simplemente agregue .htaccess
<script>
System.config({ baseURL: ''/'' });
</script>
fuente: https://gist.github.com/alexsasharegan/173878f9d67055bfef63449fa7136042
Este tema es un poco antiguo y está resuelto, pero me gustaría sugerirle una solución simple, clara y mejor. Funciona si usas un servidor web.
Cada servidor web tiene la capacidad de redirigir al usuario a una página de error en el caso de http 404. Para resolver este problema, debe redirigir al usuario a la página de índice.
Si utiliza el servidor base Java (tomcat o cualquier servidor de aplicaciones java), la solución podría ser la siguiente:
web.xml:
<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />
Ejemplo:
- OBTENGA http://example.com/about
- El servidor web arroja http 404 porque esta página no existe en el lado del servidor
- la configuración de la página de error le dice al servidor que envía la página index.jsp al usuario
- entonces JS hará el resto del trabajo en el lado del cliente porque la url en el lado del cliente todavía es http://example.com/about .
Eso es todo, no más necesidades mágicas :)
Esto puede resolver tu problema
También enfrenté el mismo problema en la aplicación ReactJS en modo Producción. Aquí está la 2 solución al problema.
1.Cambie el historial de enrutamiento a "hashHistory" en lugar de browserHistory en lugar de
<Router history={hashHistory} >
<Route path="/home" component={Home} />
<Route path="/aboutus" component={AboutUs} />
</Router>
Ahora compila la aplicación usando el comando
sudo npm run build
Luego coloque la carpeta de compilación en su carpeta var / www /. Ahora la aplicación está funcionando bien con la adición de la etiqueta # en todas y cada una de las URL. me gusta
localhost / # / home localhost / # / aboutus
Solución 2: Sin la etiqueta # usando browserHistory,
Establezca su historial = {browserHistory} en su enrutador, ahora compílelo usando sudo npm run build.
Debe crear el archivo "conf" para resolver la página 404 no encontrada, el archivo conf debería ser así.
abre tu terminal escribe los siguientes comandos
cd / etc / apache2 / sites-available ls nano sample.conf Agregue el contenido a continuación.
<VirtualHost *:80>
ServerAdmin [email protected]
ServerName 0.0.0.0
ServerAlias 0.0.0.0
DocumentRoot /var/www/html/
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory "/var/www/html/">
Options Indexes FollowSymLinks
AllowOverride all
Require all granted
</Directory>
</VirtualHost>
Ahora debe habilitar el archivo sample.conf utilizando el siguiente comando
cd /etc/apache2/sites-available
sudo a2ensite sample.conf
luego le pedirá que vuelva a cargar el servidor apache, utilizando el servicio sudo apache2 reload o restart
luego abra su carpeta localhost / build y agregue el archivo .htaccess con el contenido de abajo.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^.*$ / [L,QSA]
Ahora la aplicación funciona normalmente.
Nota: cambie 0.0.0.0 ip a su dirección IP local.
Si tiene alguna duda al respecto, no dude en hacer un comentario.
Espero que sea útil para los demás.
Intente agregar el archivo ".htaccess" dentro de la carpeta pública con el siguiente código.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- WELCOME FILE LIST -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- ERROR PAGES DEFINITION -->
<error-page>
<error-code>404</error-code>
<location>/index.jsp</location>
</error-page>
</web-app>
Las respuestas aquí son extremadamente útiles, lo que funcionó para mí fue configurar mi servidor Webpack para esperar las rutas.
devServer: {
historyApiFallback: true,
contentBase: ''./'',
hot: true
},
El historyApiFallback es lo que solucionó este problema para mí. Ahora el enrutamiento funciona correctamente y puedo actualizar la página o escribir la URL directamente. No necesita preocuparse por las soluciones en su servidor de nodo. Esta respuesta obviamente solo funciona si estás usando webpack.
EDITAR: vea mi respuesta aquí para obtener una razón más detallada de por qué esto es necesario: https://.com/a/37622953/5217568
Para usuarios de React Router V4 :
Si intenta resolver este problema mediante la técnica Hash History mencionada en otras respuestas, tenga en cuenta que
<Router history={hashHistory} >
no funciona en V4, utilice
HashRouter
en
HashRouter
lugar:
RewriteBase /
RewriteRule ^index/.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
Referencia: HashRouter
Puede cambiar su htaccess e insertar esto:
import { HashRouter } from ''react-router-dom''
<HashRouter>
<App/>
</HashRouter>
Estoy usando
react: "^15.3.2"
,
react-router: "^3.0.0"
,
history: "^4.3.0"
,
Se puede llamar al enrutador de dos maneras diferentes, dependiendo de si la navegación se produce en el cliente o en el servidor. Lo tiene configurado para la operación del lado del cliente. El parámetro clave es el segundo del método de ejecución , la ubicación.
Cuando usa el componente React Router Link, bloquea la navegación del navegador y llama a la transición a para hacer una navegación del lado del cliente. Está utilizando HistoryLocation, por lo que utiliza la API de historial HTML5 para completar la ilusión de navegación simulando la nueva URL en la barra de direcciones. Si está utilizando navegadores antiguos, esto no funcionará. Debería usar el componente HashLocation.
Cuando presiona actualizar, omite todo el código React y React Router.
El servidor recibe la solicitud de
/joblist
y debe devolver algo.
En el servidor, debe pasar la ruta solicitada al método de
run
para que presente la vista correcta.
Puede usar el mismo mapa de ruta, pero probablemente necesitará una llamada diferente a
Router.run
.
Como señala Charles, puede usar la reescritura de URL para manejar esto.
Otra opción es usar un servidor node.js para manejar todas las solicitudes y pasar el valor de la ruta como argumento de ubicación.
En express, por ejemplo, podría verse así:
var app = express();
app.get(''*'', function (req, res) { // This wildcard method handles all requests
Router.run(routes, req.path, function (Handler, state) {
var element = React.createElement(Handler);
var html = React.renderToString(element);
res.render(''main'', { content: html });
});
});
Tenga en cuenta que la ruta de solicitud se pasa para
run
.
Para hacer esto, necesitará tener un motor de vista del lado del servidor al que pueda pasar el HTML renderizado.
Hay una serie de otras consideraciones al usar
renderToString
y al ejecutar React en el servidor.
Una vez que la página se procesa en el servidor, cuando su aplicación se carga en el cliente, se volverá a procesar, actualizando el HTML procesado del lado del servidor según sea necesario.
Si aloja su aplicación de reacción en IIS, simplemente agregue un archivo web.config que contenga:
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
RewriteRule ^ /index.html [L]
Esto le indicará al servidor IIS que devuelva la página principal al cliente en lugar del error 404 y no es necesario usar el historial de hash.
Si tiene una copia de seguridad de su index.html, asegúrese de que en su archivo index.html tenga esto:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
</configuration>
Esto puede diferir de un proyecto a otro.
Todavía no estoy usando el renderizado del lado del servidor, pero me encontré con el mismo problema que el OP donde Link parecía funcionar bien la mayor parte del tiempo pero fallaba cuando tenía un parámetro. Documentaré mi solución aquí para ver si ayuda a alguien.
Mi jsx principal contiene esto:
{
"hosting": {
"rewrites": [{
"source":"**",
"destination": "/index.html"
}]
}
}
Esto funciona bien para el primer enlace coincidente, pero cuando: id cambia en las expresiones
<Link>
anidadas en la página de detalles de ese modelo, la url cambia en la barra del navegador pero el contenido de la página no cambió inicialmente para reflejar el modelo vinculado.
El problema era que había usado
props.params.id
para configurar el modelo en
componentDidMount
.
El componente solo se monta una vez, por lo que esto significa que el primer modelo es el que se pega en la página y los enlaces posteriores cambian los accesorios, pero dejan la página sin cambios.
Establecer el modelo en el estado del componente tanto en
componentDidMount
como en
componentWillReceiveProps
(donde se basa en los siguientes accesorios) resuelve el problema y el contenido de la página cambia para reflejar el modelo deseado.
Utilicé create-react-app para hacer un sitio web en este momento y tuve el mismo problema presentado aquí.
Uso
BrowserRouting
del
BrowserRouting
react-router-dom
.
Estoy ejecutando en un servidor Nginx y lo que me resolvió fue agregar lo siguiente a
/etc/nginx/yourconfig.conf
location / {
if (!-e $request_filename){
rewrite ^(.*)$ /index.html break;
}
}
Lo que corresponde a agregar lo siguiente al
.htaccess
en caso de que esté ejecutando Appache
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
Esta también parece ser la solución sugerida por Facebook y se puede encontrar here
En caso de que alguien esté buscando una solución en React JS SPA con Laravel.
La respuesta aceptada es la mejor explicación de por qué ocurren tales problemas.
Como ya se explicó, debe configurar tanto el lado del cliente como el del servidor.
En su plantilla Blade, incluya el archivo incluido js, asegúrese de usarlo
URL facade
así
<script src="{{ URL::to(''js/user/spa.js'') }}"></script>
En sus rutas, asegúrese de agregar esto al punto final principal donde está la plantilla de la hoja. Por ejemplo,
Route::get(''/setting-alerts'', function () {
return view(''user.set-alerts'');
});
Lo anterior es el punto final principal para la plantilla de la hoja. Ahora agregue una ruta opcional también,
Route::get(''/setting-alerts/{spa?}'', function () {
return view(''user.set-alerts'');
});
El problema que ocurre es que primero se carga la plantilla de la cuchilla, luego el enrutador de reacción.
Entonces, cuando está cargando
''/setting-alerts''
, carga el html y el js.
Pero cuando carga
''/setting-alerts/about''
, primero se carga en el lado del servidor.
Como en el lado del servidor, no hay nada en esta ubicación, devuelve no encontrado.
Cuando tiene ese enrutador opcional, carga la misma página y también se carga el enrutador de reacción, luego el cargador de reacción decide qué componente mostrar.
Espero que esto ayude.
Pila de producción: React, React Router v4, BrowswerRouter, Express, Nginx
1) User BrowserRouter para urls bonitas
// server.js
app.get(''/*'', function(req, res) {
res.sendFile(path.join(__dirname, ''path/to/your/index.html''), function(err) {
if (err) {
res.status(500).send(err)
}
})
})
2) Agregue index.html a todas las solicitudes desconocidas utilizando
/*
location / {
try_files $uri /index.html;
}
3) paquete webpack con
webpack -p
4) ejecute
nodemon server.js
o
node server.js
EDITAR: es posible que desee dejar que nginx maneje esto en el bloque del servidor y no tenga en cuenta el paso 2:
devServer: {
historyApiFallback: true
}
Si aloja una aplicación de reacción a través de AWS Static S3 Hosting y CloudFront
Este problema se presentó cuando CloudFront respondió con un mensaje 403 de acceso denegado porque esperaba que / some / other / path existiera en mi carpeta S3, pero esa ruta solo existe internamente en el enrutamiento de React con react-router.
La solución fue configurar una regla de páginas de error de distribución. Vaya a la configuración de CloudFront y elija su distribución. A continuación, vaya a la pestaña "Páginas de error". Haga clic en "Crear respuesta de error personalizada" y agregue una entrada para 403 ya que ese es el código de estado de error que obtenemos. Establezca la Ruta de la página de respuesta en /index.html y el código de estado en 200. El resultado final me sorprende con su simplicidad. La página de índice se sirve, pero la URL se conserva en el navegador, por lo que una vez que se carga la aplicación de reacción, detecta la ruta de la URL y navega hacia la ruta deseada.
Si está utilizando la aplicación Create React:
Sin embargo, hay un gran recorrido por este problema con soluciones para muchas de las principales plataformas de alojamiento que puede encontrar HERE en la página Crear aplicación React. Por ejemplo, uso React Router v4 y Netlify para mi código de interfaz. Todo lo que se necesitó fue agregar 1 archivo a mi carpeta pública ("_redirects") y una línea de código en ese archivo:
/* /index.html 200
Ahora mi sitio web representa correctamente rutas como mysite.com/pricing cuando se ingresa al navegador o cuando alguien presiona actualizar.
Al observar los comentarios sobre la respuesta aceptada y la naturaleza genérica de esta pregunta (''no funciona''), pensé que este podría ser un buen lugar para algunas explicaciones generales sobre los problemas involucrados aquí. Por lo tanto, esta respuesta pretende ser información / elaboración de antecedentes sobre el caso de uso específico del OP. Por favor, tenga paciencia conmigo.
Lado del servidor vs lado del cliente
Lo primero que hay que entender sobre esto es que ahora hay 2 lugares donde se interpreta la URL, mientras que solía haber solo 1 en ''los viejos tiempos''.
En el pasado, cuando la vida era simple, algún usuario enviaba una solicitud de
http://example.com/about
al servidor, que inspeccionaba la parte de la ruta de la URL, determinaba que el usuario estaba solicitando la página acerca y luego la enviaba de vuelta página.
Con el enrutamiento del lado del cliente, que es lo que proporciona React-Router, las cosas son menos simples.
Al principio, el cliente aún no tiene ningún código JS cargado.
Entonces, la primera solicitud siempre será al servidor.
Eso devolverá una página que contiene las etiquetas de secuencia de comandos necesarias para cargar React y React Router, etc. Solo cuando se hayan cargado esas secuencias de comandos comienza la fase 2.
En la fase 2, cuando el usuario hace clic en el enlace de navegación ''Acerca de nosotros'', por ejemplo, la URL se cambia
localmente solo
a
http://example.com/about
(hecho posible por la
API de historial
), pero
no se solicita al servidor esta hecho
.
En cambio, React Router hace lo suyo en el lado del cliente, determina qué vista de React renderizar y lo representa.
Asumiendo que su página acerca de no necesita hacer llamadas REST, ya está hecha.
Ha pasado de Inicio a Acerca de nosotros sin que se haya activado ninguna solicitud del servidor.
Básicamente, cuando hace clic en un enlace, se ejecuta un Javascript que manipula la URL en la barra de direcciones, sin causar una actualización de la página , lo que a su vez hace que React Router realice una transición de página en el lado del cliente .
Pero ahora considere lo que sucede si copia y pega la URL en la barra de direcciones y se la envía por correo electrónico a un amigo.
Tu amigo aún no ha cargado tu sitio web.
En otras palabras, ella todavía está en la
fase 1
.
Todavía no se está ejecutando ningún React Router en su máquina.
Por lo tanto, su navegador realizará una
solicitud del servidor
a
http://example.com/about
.
Y aquí es donde comienza tu problema.
Hasta ahora, podría salirse con la simple colocación de un HTML estático en la raíz web de su servidor.
Pero eso daría errores
404
para todas las otras URL
cuando se solicite desde el servidor
.
Esas mismas URL funcionan bien
en el lado del cliente
, porque React Router está haciendo el enrutamiento por usted, pero fallan
en el lado del servidor a
menos que haga que su servidor las entienda.
Combinación de enrutamiento del lado del servidor y del cliente
Si desea que la
http://example.com/about
URL funcione tanto en el lado del servidor como del lado del cliente, debe configurar rutas para ella tanto en el lado del servidor como del lado del cliente.
Tiene sentido ¿verdad?
Y aquí es donde comienzan tus elecciones. Las soluciones van desde eludir el problema por completo, a través de una ruta general que devuelve el HTML de arranque, hasta el enfoque isomorfo completo en el que tanto el servidor como el cliente ejecutan el mismo código JS.
.
Evitando el problema por completo: Hash History
Con
Hash History en
lugar de
Browser History
, su URL para la página acerca se vería así:
http://example.com/#/about
La parte después del símbolo hash (
#
) no se envía al servidor.
Entonces, el servidor solo ve
http://example.com/
y envía la página de índice como se esperaba.
React-Router recogerá la parte
#/about
y mostrará la página correcta.
Desventajas :
- URL ''feas''
- La representación del lado del servidor no es posible con este enfoque. En lo que respecta a la optimización de motores de búsqueda (SEO), su sitio web consta de una sola página con casi ningún contenido.
.
Catch-all
Con este enfoque, sí utiliza el historial del navegador, pero simplemente configura un conjunto en el servidor que envía
/*
a
index.html
, lo que le proporciona prácticamente la misma situación que con el historial de hash.
Sin embargo, tiene URL limpias y podría mejorar este esquema más adelante sin tener que invalidar todos los favoritos de sus usuarios.
Desventajas :
- Más complejo de configurar
- Todavía no hay buen SEO
.
Híbrido
En el enfoque híbrido, se expande en el escenario general al agregar secuencias de comandos específicas para rutas específicas. Puede crear algunos scripts PHP simples para devolver las páginas más importantes de su sitio con contenido incluido, de modo que Googlebot pueda al menos ver lo que hay en su página.
Desventajas :
- Aún más complejo de configurar
- Solo buen SEO para esas rutas le das el trato especial
- Duplicación de código para representar contenido en el servidor y el cliente
.
Isomorfo
¿Qué sucede si utilizamos Node JS como nuestro servidor para poder ejecutar el mismo código JS en ambos extremos? Ahora, tenemos todas nuestras rutas definidas en una única configuración de enrutador de reacción y no necesitamos duplicar nuestro código de representación. Este es ''el santo grial'', por así decirlo. El servidor envía exactamente el mismo marcado que terminaríamos si la transición de página hubiera ocurrido en el cliente. Esta solución es óptima en términos de SEO.
Desventajas :
- El servidor debe (poder) ejecutar JS. He experimentado con Java icw Nashorn pero no me funciona. En la práctica, significa principalmente que debe utilizar un servidor basado en Node JS.
-
Muchos problemas ambientales difíciles (uso de la
window
en el lado del servidor, etc.) - Curva de aprendizaje empinada
.
¿Cuál debería usar?
Elija el que pueda salirse con la suya. Personalmente, creo que el conjunto es lo suficientemente simple como para configurarlo, por lo que ese sería mi mínimo. Esta configuración le permite mejorar las cosas con el tiempo. Si ya está utilizando Node JS como su plataforma de servidor, definitivamente investigaría hacer una aplicación isomórfica. Sí, es difícil al principio, pero una vez que lo dominas, en realidad es una solución muy elegante para el problema.
Básicamente, para mí, ese sería el factor decisivo. Si mi servidor se ejecuta en el Nodo JS, me volvería isomorfo; de lo contrario, optaría por la solución Catch-all y solo la ampliaría (solución híbrida) a medida que pasa el tiempo y los requisitos de SEO lo exigen.
Si desea obtener más información sobre el renderizado isomorfo (también llamado ''universal'') con React, hay algunos buenos tutoriales sobre el tema:
- Reacciona al futuro con aplicaciones isomorfas
- El dolor y la alegría de crear aplicaciones isomorfas en ReactJS
- Cómo implementar Node + React JavaScript isomorfo y por qué es importante
Además, para comenzar, te recomiendo mirar algunos kits de inicio. Elija uno que coincida con sus opciones para la pila de tecnología (recuerde, React es solo la V en MVC, necesita más cosas para construir una aplicación completa). Comience mirando el publicado por el propio Facebook:
O elija uno de los muchos de la comunidad. Hay un buen sitio ahora que intenta indexarlos a todos:
Empecé con estos:
Actualmente estoy usando una versión casera de renderizado universal inspirada en los dos kits de inicio anteriores, pero ahora están desactualizados.
¡Buena suerte con tu búsqueda!