Come interrompere / # / nel browser con React-Router?


103

Qualche modo per impedire che venga /#/visualizzato nella barra degli indirizzi del browser quando si utilizza React-Router? Questo è con ReactJS. Ad esempio, facendo clic sui collegamenti per passare a un nuovo percorso viene visualizzato localhost:3000/#/o localhost:3000/#/about. A seconda del percorso.


1
È dovuto all'uso di HashHistoryiso BrowserHistory. Vedi anche questa domanda SO in cui fornisco molte informazioni di base su questo argomento.
Stijn de Witt

Risposte:


78

Per le versioni 1, 2 e 3 di react-router, il modo corretto per impostare il percorso sullo schema di mappatura URL è passare un'implementazione della cronologia nel historyparametro di <Router>. Dalla documentazione delle storie :

In poche parole, una cronologia sa come ascoltare la barra degli indirizzi del browser per le modifiche e analizza l'URL in un oggetto posizione che il router può utilizzare per abbinare le rotte e rendere l'insieme corretto di componenti.

Versioni 2 e 3

In react-router 2 e 3, il codice di configurazione del percorso sarà simile a questo:

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

Versione 1

Nella versione 1.x, utilizzerai invece quanto segue:

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

Fonte: Guida all'aggiornamento alla versione 2.0

Versione 4

Per la prossima versione 4 di react-router, la sintassi è cambiata molto ed è necessario utilizzarla BrowserRoutercome tag root del router.

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

Fonte Reagire Router versione 4 Documenti


6
Nota che historyè un pacchetto autonomo che dovrai installare.
Jan Klimo

4
Hanno cambiato browserHistoryin v2.x: import { browserHistory } from 'react-router' <Router history={browserHistory} />Controlla la guida all'aggiornamento del router react
pistou

Grazie @pistou, ho aggiornato la risposta alla versione 2.0!
Adam Brown

1
Per hashHistory, c'è un modo per sbarazzarsi di questa query param alla fine? http://localhost:8080/#/dashboard?_k=yqwtyu
Con Antonakos

2
@Matt Funziona, ma richiede il supporto sul server. Questo perché quando aggiorni, colpisci il server con un URL con percorso.
Stijn de Witt

40
Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

Per la versione corrente 0.11 e successive, è necessario aggiungere Router.HistoryLocationa Router.run(). <Routes>sono ora deprecati. Vedere la Guida all'aggiornamento per l'implementazione di HistoryLocation 0.12.x.


1
questo ha completamente rovinato la mia app. sembra che la loro attuale implementazione sia difettosa?
ninjaneer

2
@Ninja forse pubblica una nuova domanda con i numeri di versione esatti per React e React-Router, codice fallito ed errori ricevuti.
pxwise

@Chet Sembra che i documenti di react-router siano stati mescolati. Collegamento aggiornato all'unico riferimento per HistoryLocation trovato nella Guida all'aggiornamento.
pxwise

21

Se non è necessario supportare IE8, è possibile utilizzare la cronologia del browser e la reazione-router utilizzerà window.pushStateinvece di impostare l'hash.

Come farlo esattamente dipende dalla versione di React Router che stai utilizzando:


Grazie @ ben-alpert, ora ho capito.
Giant Elk

1
Ho aggiunto <Routes location="history">che tutto funziona bene, finché non aggiorni il browser durante il percorso, cioè localhost:3000/aboutricevo un errore 404. È quello previsto, sto usando python -m SimpleHTTPServer 3000?
Giant Elk

5
Devi assicurarti che il tuo server possa gestire l'URL dello stato push. In questo caso probabilmente significa che devi solo assicurarti che qualunque cosa stia servendo la tua app invii sempre ogni URL che arriva alla stessa radice. Quindi questo /aboutcarica effettivamente la tua pagina principale /. Altrimenti il ​​tuo server sta cercando di cercare un percorso che corrisponda /aboute non trova nulla (404). Personalmente non uso python ma di solito trovi o un percorso manuale per /*o /.*-> /funziona - o potrebbe essere qualcosa chiamato html5ModeURL nelle impostazioni del tuo server.
Mike Driver

3
IE9 non supporta neanche pushState, quindi è davvero "Se non hai bisogno di supportare IE9", giusto? Vorrei aver sbagliato.
Cymen

1
Quel collegamento GitHub è una pagina non trovata ora.
k00k

9

Puoi effettivamente usare .htaccess per farlo. Il browser normalmente necessita del delimitatore della stringa di query ?o #per determinare dove inizia la stringa di query e dove finiscono i percorsi della directory. Il risultato finale che vogliamo è www.mysite.com/dir Quindi dobbiamo individuare il problema prima che il server web cerchi la directory che pensa di aver richiesto /dir. Quindi mettiamo un .htaccessfile nella radice del progetto.

    # 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

Quindi ottieni i parametri della query con window.location.pathname

Puoi quindi evitare di utilizzare i percorsi di reazione se lo desideri e manipolare anche l'URL e la cronologia del browser se lo desideri. Spero che questo aiuti qualcuno ...


Qual è l'equivalente per Jboss?
Raghavan

5

Installa il pacchetto della cronologia

npm install history --save

Quindi importa createHistory e useBasename dalla cronologia

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

se l'URL della tua app è www.example.com/myApp, / root dovrebbe essere / myApp.

passare la variabile della cronologia al router

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

Ora per tutti i tuoi tag Link aggiungi una "/" davanti a tutti i percorsi.

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

L'ispirazione della soluzione è venuta da React-Router che, sfortunatamente, non è stato adeguatamente documentato nella loro API.


questo richiede un server nodo? Sto cercando di ottenere lo stesso stile di URL ma solo dal lato client. È possibile?
Sebastialonso

1
no, non hai bisogno di un server del nodo. In effetti sto funzionando sul backend di django. Ma probabilmente hai bisogno di node per gli strumenti.
Mox

1
Ok, ho provato questo approccio. Quando premo F5, tutto quello che ottengo è "Non trovato". È possibile che questa storia si occupi di questo?
Sebastialonso

se non viene trovato, viene restituito dal server. questo significa che il pattern URL non fa parte di React router.
Mox

1
Sì, dopo aver letto un po 'di più, tutto è diventato chiaro. Ho finito per andare con hashHistory senza le chiavi.
Sebastialonso

3

Un altro modo per gestire cosa visualizzare dopo l'hash (quindi se non usi pushState!) È creare il tuo CustomLocation e caricarlo durante la creazione di ReactRouter.

Ad esempio, se desideri che l'URL hashbang (quindi con #!) Sia conforme alle specifiche di Google per la scansione, puoi creare un file HashbangLocation.js che copia principalmente l'HashLocation originale come:

'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;

Nota la funzione slashToHashbang .

Allora devi solo fare

ReactRouter.create({location: HashbangLocation})

Ed è tutto :-)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.