¿Cómo detener / # / en el navegador con react-router?

103

¿Alguna forma de evitar que se /#/muestre en la barra de direcciones del navegador cuando se usa react-router? Eso es con ReactJS. es decir, al hacer clic en los enlaces para ir a una nueva ruta se muestra localhost:3000/#/o localhost:3000/#/about. Dependiendo de la ruta.

Alce gigante
fuente
1
Se debe al uso de HashHistoryiso BrowserHistory. Vea también esta pregunta SO donde doy mucha información de fondo sobre este tema.
Stijn de Witt

Respuestas:

78

Para las versiones 1, 2 y 3 de react-router, la forma correcta de establecer la ruta al esquema de mapeo de URL es pasando una implementación de historial en el historyparámetro de <Router>. De la documentación de historias :

En pocas palabras, un historial sabe cómo escuchar los cambios en la barra de direcciones del navegador y analiza la URL en un objeto de ubicación que el enrutador puede usar para hacer coincidir rutas y representar el conjunto correcto de componentes.

Versiones 2 y 3

En react-router 2 y 3, el código de configuración de su ruta se verá así:

import { browserHistory } from 'react-router'
ReactDOM.render (( 
 <Router history={browserHistory} >
   ...
 </Router> 
), document.body);

Versión 1

En la versión 1.x, utilizará en su lugar lo siguiente:

import createBrowserHistory from 'history/lib/createBrowserHistory'
ReactDOM.render (( 
  <Router history={createBrowserHistory()} >
   ...
  </Router> 
), document.body);

Fuente: Guía de actualización de la versión 2.0

Versión 4

Para la próxima versión 4 de react-router, la sintaxis ha cambiado mucho y es necesario utilizarla BrowserRoutercomo etiqueta raíz del router.

import BrowserRouter from 'react-router/BrowserRouter'
ReactDOM.render (( 
  <BrowserRouter>
   ...
 <BrowserRouter> 
), document.body);

Source React Router Versión 4 Docs

Adam Brown
fuente
6
Tenga en cuenta que historyes un paquete independiente que deberá instalar.
Jan Klimo
4
Cambiaron el browserHistoryen v2.x: import { browserHistory } from 'react-router' <Router history={browserHistory} />Verifique la guía de actualización del enrutador react
pistou
Gracias @pistou, ¡actualicé la respuesta a la versión 2.0!
Adam Brown
1
Porque hashHistory, ¿hay alguna manera de deshacerse de este parámetro de consulta al final? http://localhost:8080/#/dashboard?_k=yqwtyu
Con Antonakos
2
@Matt Funciona, pero requiere soporte en el servidor. Eso es porque cuando actualiza, ingresa al servidor con una URL con ruta.
Stijn de Witt
40
Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

Para la versión actual 0,11 y hacia adelante, es necesario agregar Router.HistoryLocationa Router.run(). <Routes>ahora están en desuso. Consulte la Guía de actualización para la implementación de la ubicación de historial 0.12.x.

pxwise
fuente
1
esto arruinó completamente mi aplicación. parece que su implementación actual tiene errores?
ninjaneer
2
@Ninja quizás publique una nueva pregunta con números de versión exactos para react y react-router, código fallado y errores recibidos.
pxwise
@Chet Parece que los documentos react-router se han barajado. Enlace actualizado a la única referencia para HistoryLocation que se encuentra en la Guía de actualización.
pxwise
21

Si no necesita admitir IE8, puede usar el historial del navegador y react-router usará en window.pushStatelugar de configurar el hash.

La forma exacta de hacer esto depende de la versión de React Router que esté utilizando:

Sophie Alpert
fuente
Gracias @ ben-alpert, lo entiendo ahora.
Alce gigante
1
Agregué <Routes location="history">que todo funciona bien, hasta que actualice el navegador cuando esté en la ruta, es decir localhost:3000/about, recibo un error 404. ¿Es eso esperado, estoy usando python -m SimpleHTTPServer 3000?
Alce gigante
5
Debe asegurarse de que su servidor pueda manejar la URL de estado de inserción. En este caso, probablemente significa que solo necesita asegurarse de que lo que sea que esté sirviendo a su aplicación siempre envíe todas las URL que recibe a la misma raíz. Entonces eso /aboutrealmente carga su página raíz /. De lo contrario, su servidor está tratando de buscar una ruta que coincida /abouty no encuentra nada (404). Personalmente, no uso Python, pero generalmente encuentra una ruta manual para /*o /.*-> /funciona - o podría ser algo llamado html5ModeURL en la configuración de su servidor.
Mike Driver
3
IE9 tampoco es compatible con pushState, por lo que en realidad es "Si no necesita ser compatible con IE9", ¿verdad? Ojalá estuviera equivocado.
Cymen
1
Ese enlace de github es una página que no se encuentra ahora.
k00k
9

De hecho, puede usar .htaccess para lograr esto. El navegador normalmente necesita el delimitador de la cadena de consulta ?o #para determinar dónde comienza la cadena de consulta y dónde terminan las rutas del directorio. El resultado final que queremos es www.mysite.com/dir Entonces, debemos detectar el problema antes de que el servidor web busque el directorio que cree que solicitamos /dir. Entonces colocamos un .htaccessarchivo en la raíz del proyecto.

    # Setting up apache options
    AddDefaultCharset utf-8
    Options +FollowSymlinks -MultiViews -Indexes
    RewriteEngine on

    # Setting up apache options (Godaddy specific)
    #DirectoryIndex index.php
    #RewriteBase /


    # Defining the rewrite rules
    RewriteCond %{SCRIPT_FILENAME} !-d
    RewriteCond %{SCRIPT_FILENAME} !-f

    RewriteRule ^.*$ ./index.html

Luego obtienes los parámetros de consulta con window.location.pathname

Luego puede evitar el uso de rutas de reacción si lo desea y simplemente manipular la URL y el historial del navegador si lo desea también. Espero que esto ayude a alguien...

Garrett Tacoronte
fuente
¿Cuál es el equivalente de Jboss?
Raghavan
5

Instale el paquete de historial

npm install history --save

A continuación, importe createHistory y useBasename del historial

import { createHistory, useBasename } from 'history';
...
const history = useBasename(createHistory)({
  basename: '/root' 
});

si la URL de su aplicación es www.example.com/myApp, entonces / root debería ser / myApp.

pasar la variable de historial al enrutador

render((
  <Router history={history}>
    ...
  </Router>
), document.getElementById('example'));

Ahora, para todas sus etiquetas de enlace, agregue una "/" delante de todas las rutas.

<Link to="/somewhere">somewhere</Link>

La inspiración de la solución provino del ejemplo de React-Router que, desafortunadamente, no se documentó adecuadamente en su API.

Mox
fuente
¿Requiere esto un servidor de nodo? Estoy tratando de lograr el mismo estilo de URL pero solo a través del lado del cliente. ¿Es posible?
Sebastialonso
1
no, no necesita un servidor de nodo. De hecho, estoy ejecutando en django backend. Pero probablemente necesite un nodo para las herramientas.
Mox
1
Ok, probé este enfoque. Cuando presiono F5, todo lo que obtengo es "No encontrado". ¿Es posible que esta historia se ocupe de eso?
Sebastialonso
si no se encuentra u, el servidor lo devuelve. esto significa que el patrón de URL no es parte de react router.
Mox
1
Sí, después de leer un poco más, todo quedó claro. Terminé yendo con hashHistory sin las claves.
Sebastialonso
3

Otra forma de manejar lo que se muestra después del hash (¡si no usa pushState!) Es crear su CustomLocation y cargarla en la creación de ReactRouter.

Por ejemplo, si desea que la URL de hashbang (¡así que con #!) Cumpla con las especificaciones de Google para el rastreo, puede crear un archivo HashbangLocation.js que copie principalmente la HashLocation original, como:

'use strict';

var LocationActions = require('../../node_modules/react-router/lib/actions/LocationActions');
var History = require('../../node_modules/react-router/lib/History');

var _listeners = [];
var _isListening = false;
var _actionType;

function notifyChange(type) {
  if (type === LocationActions.PUSH) History.length += 1;

  var change = {
    path: HashbangLocation.getCurrentPath(),
    type: type
  };

  _listeners.forEach(function (listener) {
    listener.call(HashbangLocation, change);
  });
}

function slashToHashbang(path) {
  return "!" + path.replace(/^\//, '');
}

function ensureSlash() {

  var path = HashbangLocation.getCurrentPath();
  if (path.charAt(0) === '/') {
    return true;
  }HashbangLocation.replace('/' + path);

  return false;
}

function onHashChange() {
  if (ensureSlash()) {
    // If we don't have an _actionType then all we know is the hash
    // changed. It was probably caused by the user clicking the Back
    // button, but may have also been the Forward button or manual
    // manipulation. So just guess 'pop'.
    var curActionType = _actionType;
    _actionType = null;
    notifyChange(curActionType || LocationActions.POP);
  }
}

/**
 * A Location that uses `window.location.hash`.
 */
var HashbangLocation = {

  addChangeListener: function addChangeListener(listener) {
    _listeners.push(listener);

    // Do this BEFORE listening for hashchange.
    ensureSlash();

    if (!_isListening) {
      if (window.addEventListener) {
        window.addEventListener('hashchange', onHashChange, false);
      } else {
        window.attachEvent('onhashchange', onHashChange);
      }

      _isListening = true;
    }
  },

  removeChangeListener: function removeChangeListener(listener) {
    _listeners = _listeners.filter(function (l) {
      return l !== listener;
    });

    if (_listeners.length === 0) {
      if (window.removeEventListener) {
        window.removeEventListener('hashchange', onHashChange, false);
      } else {
        window.removeEvent('onhashchange', onHashChange);
      }

      _isListening = false;
    }
  },

  push: function push(path) {
    _actionType = LocationActions.PUSH;
    window.location.hash = slashToHashbang(path);
  },

  replace: function replace(path) {
    _actionType = LocationActions.REPLACE;
    window.location.replace(window.location.pathname + window.location.search + '#' + slashToHashbang(path));
  },

  pop: function pop() {
    _actionType = LocationActions.POP;
    History.back();
  },

  getCurrentPath: function getCurrentPath() {
    return decodeURI(
    // We can't use window.location.hash here because it's not
    // consistent across browsers - Firefox will pre-decode it!
    "/" + (window.location.href.split('#!')[1] || ''));
  },

  toString: function toString() {
    return '<HashbangLocation>';
  }

};

module.exports = HashbangLocation;

Tenga en cuenta la función slashToHashbang .

Entonces solo tienes que hacer

ReactRouter.create({location: HashbangLocation})

Y eso es :-)

Jonathan Banon
fuente