Wie stoppe ich / # / im Browser mit dem React-Router?

103

Gibt es eine Möglichkeit zu verhindern, dass /#/bei Verwendung des React-Routers in der Adressleiste des Browsers angezeigt wird? Das ist mit ReactJS. dh Klicken Sie auf Links, um zu einer neuen Route zu gelangen, localhost:3000/#/oder localhost:3000/#/about. Abhängig von der Route.

Riesenelch
quelle
1
Es liegt an der Verwendung von HashHistoryISO BrowserHistory. Siehe auch diese SO-Frage, in der ich viele Hintergrundinformationen zu diesem Thema gebe.
Stijn de Witt

Antworten:

78

Für die Versionen 1, 2 und 3 des React-Routers besteht die richtige Methode zum Festlegen der Route zum URL-Zuordnungsschema darin, eine Verlaufsimplementierung an den historyParameter von zu übergeben <Router>. Aus der Historien-Dokumentation :

Kurz gesagt, ein Verlauf weiß, wie er die Adressleiste des Browsers auf Änderungen überwacht und die URL in ein Standortobjekt analysiert, mit dem der Router Routen abgleichen und die richtigen Komponenten rendern kann.

Versionen 2 und 3

In React-Router 2 und 3 sieht Ihr Routenkonfigurationscode ungefähr so ​​aus:

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

Version 1

In Version 1.x verwenden Sie stattdessen Folgendes:

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

Quelle: Upgrade-Handbuch für Version 2.0

Version 4

Für die kommende Version 4 von React-Router hat sich die Syntax stark geändert und es ist erforderlich, sie BrowserRouterals Router-Root-Tag zu verwenden.

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

Source React Router Version 4 Docs

Adam Brown
quelle
6
Beachten Sie, dass dies historyein eigenständiges Paket ist, das Sie installieren müssen.
Jan Klimo
4
Sie änderten die browserHistoryin v2.x: import { browserHistory } from 'react-router' <Router history={browserHistory} />Überprüfen Sie React -Router Upgrade-Anleitung
Pistou
Danke @pistou, ich habe die Antwort auf Version 2.0 aktualisiert!
Adam Brown
1
Denn hashHistory, ist es eine Möglichkeit , am Ende dieser Abfrage param loswerden? http://localhost:8080/#/dashboard?_k=yqwtyu
Con Antonakos
2
@Matt Es funktioniert, erfordert jedoch Unterstützung auf dem Server. Dies liegt daran, dass Sie beim Aktualisieren den Server mit einer URL mit Pfad aufrufen.
Stijn de Witt
40
Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

Für die aktuelle Version 0.11 und nach vorn, müssen Sie hinzufügen Router.HistoryLocationzu Router.run(). <Routes>sind jetzt veraltet. Informationen zur Implementierung von 0.12.x HistoryLocation finden Sie im Upgrade-Handbuch .

pxwise
quelle
1
Das hat meine App komplett ruiniert. sieht aus wie ihre aktuelle Implementierung fehlerhaft ist?
Ninjaneer
2
@Ninja poste vielleicht eine neue Frage mit genauen Versionsnummern für React und React-Router, fehlerhaften Code und empfangenen Fehlern.
pxwise
@Chet Sieht so aus, als hätten React-Router-Dokumente gemischt. Aktualisierter Link zu der einzigen Referenz für HistoryLocation im Upgrade-Handbuch.
pxwise
21

Wenn Sie IE8 nicht unterstützen müssen, können Sie den Browserverlauf verwenden, und der React-Router verwendet ihn, window.pushStateanstatt den Hash festzulegen.

Wie genau dies zu tun ist, hängt von der verwendeten Version von React Router ab:

Sophie Alpert
quelle
Danke @ ben-alpert, ich verstehe es jetzt.
Riesen Elk
1
Ich habe hinzugefügt, <Routes location="history">dass alles in Ordnung ist, bis Sie den Browser auf der Route aktualisieren, dh localhost:3000/aboutdann erhalte ich einen 404-Fehler. Wird das erwartet, ich benutze python -m SimpleHTTPServer 3000?
Riesen Elk
5
Sie müssen sicherstellen, dass Ihre Serverseite die Push-Status-URL verarbeiten kann. In diesem Fall bedeutet dies wahrscheinlich, dass Sie nur sicherstellen müssen, dass alles, was Ihre App bedient, immer jede URL an denselben Stamm sendet. Damit wird /aboutIhre Root-Seite tatsächlich geladen /. Andernfalls versucht Ihr Server, nach einer Route zu suchen, die übereinstimmt, /aboutund findet nichts (404). Ich persönlich verwende Python nicht, aber normalerweise finden Sie entweder eine manuelle Route für /*oder /.*-> /funktioniert - oder es handelt sich um html5ModeURLs in Ihren Servereinstellungen.
Mike Driver
3
IE9 unterstützt auch pushState nicht - das ist also wirklich "Wenn Sie IE9 nicht unterstützen müssen", oder? Ich wünschte ich hätte mich geirrt.
Cymen
1
Dieser Github-Link ist eine Seite, die derzeit nicht gefunden wird.
k00k
9

Sie können tatsächlich .htaccess verwenden, um dies zu erreichen. Der Browser benötigt normalerweise das Trennzeichen für die Abfragezeichenfolge ?oder um #zu bestimmen, wo die Abfragezeichenfolge beginnt und die Verzeichnispfade enden. Das gewünschte Endergebnis ist: www.mysite.com/dir Wir müssen das Problem beheben, bevor der Webserver nach dem Verzeichnis sucht, nach dem er gefragt hat /dir. Also platzieren wir eine .htaccessDatei im Stammverzeichnis des Projekts.

    # 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

Anschließend erhalten Sie die Abfrageparameter mit window.location.pathname

Sie können dann die Verwendung von Reaktionsrouten vermeiden, wenn Sie möchten, und einfach die URL und den Browserverlauf bearbeiten, wenn Sie möchten. Hoffe das hilft jemandem ...

Garrett Tacoronte
quelle
Was ist das Äquivalent für Jboss?
Raghavan
5

Installieren Sie das Verlaufspaket

npm install history --save

Als nächstes importieren Sie createHistory und useBasename aus dem Verlauf

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

Wenn Ihre App-URL www.example.com/myApp lautet, sollte / root / myApp sein.

Übergeben Sie die Verlaufsvariable an den Router

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

Fügen Sie nun für alle Ihre Link-Tags ein "/" vor alle Pfade ein.

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

Die Inspiration für die Lösung kam von React-Router Example, das leider nicht richtig in ihrer API dokumentiert wurde.

Mox
quelle
Benötigt dies einen Knotenserver? Ich versuche, den gleichen URL-Stil zu erreichen, aber nur über die Clientseite. Ist es möglich?
Sebastialonso
1
Nein, Sie benötigen keinen Knotenserver. Tatsächlich laufe ich im Django-Backend. Aber Sie benötigen wahrscheinlich einen Knoten für Werkzeuge.
Mox
1
Ok, ich habe diesen Ansatz ausprobiert. Wenn ich F5 drücke, bekomme ich nur "Nicht gefunden". Kann sich diese Geschichte damit auseinandersetzen?
Sebastialonso
Wenn Sie nicht gefunden werden, wird dies vom Server zurückgegeben. Dies bedeutet, dass das URL-Muster nicht Teil des Reaktionsrouters ist.
Mox
1
Ja, nachdem ich ein bisschen mehr gelesen hatte, wurde alles klar. Am Ende ging es mit HashHistory ohne die Schlüssel.
Sebastialonso
3

Eine andere Möglichkeit, die Anzeige nach dem Hash zu handhaben (wenn Sie also pushState nicht verwenden!), Besteht darin, Ihren CustomLocation zu erstellen und ihn bei der ReactRouter-Erstellung zu laden.

Wenn Sie beispielsweise eine Hashbang-URL (also mit #!) Haben möchten, um den Google-Spezifikationen für das Crawlen zu entsprechen, können Sie eine HashbangLocation.js-Datei erstellen, die hauptsächlich die ursprüngliche HashLocation kopiert, z.

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

Beachten Sie die Funktion slashToHashbang .

Dann musst du es tun

ReactRouter.create({location: HashbangLocation})

Und das ist es :-)

Jonathan Banon
quelle