Comment arrêter / # / dans le navigateur avec react-router?

103

Un moyen d'empêcher /#/de s'afficher dans la barre d'adresse du navigateur lors de l'utilisation de react-router? C'est avec ReactJS. c'est-à-dire en cliquant sur les liens pour aller à un nouvel itinéraire montre localhost:3000/#/ou localhost:3000/#/about. Selon l'itinéraire.

Élan géant
la source
1
Cela est dû à l'utilisation d' HashHistoryiso BrowserHistory. Voir aussi cette question SO où je donne beaucoup d'informations générales sur ce sujet.
Stijn de Witt

Réponses:

78

Pour les versions 1, 2 et 3 de react-router, la manière correcte de définir l'itinéraire vers le schéma de mappage d'URL est de passer une implémentation d'historique dans le historyparamètre de <Router>. À partir de la documentation des histoires :

En un mot, un historique sait comment écouter la barre d'adresse du navigateur pour les modifications et analyse l'URL en un objet de localisation que le routeur peut utiliser pour faire correspondre les itinéraires et restituer l'ensemble correct de composants.

Versions 2 et 3

Dans react-router 2 et 3, le code de configuration de votre route ressemblera à ceci:

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

Version 1

Dans la version 1.x, vous utiliserez à la place ce qui suit:

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

Source: Guide de mise à niveau de la version 2.0

Version 4

Pour la prochaine version 4 de react-router, la syntaxe a beaucoup changé et il est nécessaire de l'utiliser BrowserRoutercomme balise racine du routeur.

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

Documentation sur le routeur source React version 4

Adam Brown
la source
6
Notez qu'il historys'agit d'un package autonome que vous devrez installer.
Jan Klimo
4
Ils ont changé la browserHistoryv2.x: import { browserHistory } from 'react-router' <Router history={browserHistory} />Vérifiez le guide de mise à niveau de
pistou
Merci @pistou, j'ai mis à jour la réponse à la version 2.0!
Adam Brown
1
Car hashHistory, y a-t-il un moyen de se débarrasser de ce paramètre de requête à la fin? http://localhost:8080/#/dashboard?_k=yqwtyu
Con Antonakos
2
@Matt Cela fonctionne, mais nécessite un support sur le serveur. En effet, lorsque vous actualisez, vous frappez le serveur avec une URL avec un chemin.
Stijn de Witt
40
Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

Pour la version actuelle 0.11 et les versions ultérieures, vous devez ajouter Router.HistoryLocationà Router.run(). <Routes>sont désormais obsolètes. Consultez le Guide de mise à niveau pour l'implémentation de HistoryLocation 0.12.x.

pxwise
la source
1
cela a complètement ruiné mon application. ressemble à leur implémentation actuelle est boguée?
ninjaneer
2
@Ninja postez peut-être une nouvelle question avec les numéros de version exacts pour react et react-router, le code défaillant et les erreurs reçues.
pxwise
@Chet On dirait que les documents de react-router ont été mélangés. Lien mis à jour vers la seule référence pour HistoryLocation trouvée dans le Guide de mise à niveau.
pxwise
21

Si vous n'avez pas besoin de prendre en charge IE8, vous pouvez utiliser l'historique du navigateur et react-router l'utilisera window.pushStateau lieu de définir le hachage.

La procédure exacte dépend de la version de React Router que vous utilisez:

Sophie Alpert
la source
Merci @ ben-alpert, je comprends maintenant.
Giant Elk
1
J'ai ajouté <Routes location="history">que tout fonctionne bien, jusqu'à ce que vous actualisiez le navigateur en route, c'est-à-dire localhost:3000/aboutque j'obtiens une erreur 404. Est-ce prévu, j'utilise python -m SimpleHTTPServer 3000?
Giant Elk
5
Vous devez vous assurer que votre serveur peut gérer l'URL de l'état push. Dans ce cas, cela signifie probablement que vous devez simplement vous assurer que tout ce qui sert votre application envoie toujours toutes les URL qu'elle obtient à la même racine. Cela /aboutcharge donc votre page racine /. Sinon, votre serveur essaie de rechercher une route qui correspond /aboutet ne trouve rien (404). Je n'utilise pas personnellement python mais vous trouvez généralement soit une route manuelle pour /*ou /.*-> /fonctionne - soit quelque chose appelé html5Modeurls dans les paramètres de votre serveur.
Mike Driver
3
IE9 ne prend pas en charge pushState non plus - donc c'est vraiment "Si vous n'avez pas besoin de prendre en charge IE9", n'est-ce pas? J'aurais aimé me tromper.
Cymen
1
Ce lien github est une page introuvable actuellement.
k00k le
9

Vous pouvez en fait utiliser .htaccess pour ce faire. Le navigateur a normalement besoin du délimiteur de chaîne de requête ?ou #pour déterminer où commence la chaîne de requête et où les chemins de répertoire se terminent. Le résultat final que nous voulons est www.mysite.com/dir donc nous devons attraper le problème avant que le serveur Web recherche le répertoire qu'il pense avoir demandé /dir. Nous plaçons donc un .htaccessfichier à la racine du projet.

    # 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

Ensuite, vous obtenez les paramètres de requête avec window.location.pathname

Vous pouvez alors éviter d'utiliser les routes de réaction si vous le souhaitez et simplement manipuler l'url et l'historique du navigateur si vous le souhaitez également. J'espère que cela aide quelqu'un ...

Garrett Tacoronte
la source
Quel est l'équivalent pour Jboss?
Raghavan
5

Installez le package d'historique

npm install history --save

Importez ensuite createHistory et useBasename depuis l'historique

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

si l'URL de votre application est www.example.com/myApp, alors / root doit être / myApp.

transmettre la variable historique au routeur

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

Maintenant, pour toutes vos balises Link, ajoutez un "/" devant tous les chemins.

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

L'inspiration de la solution est venue de l' exemple de React-Router qui, malheureusement, n'a pas été correctement documenté dans leur API.

Mox
la source
cela nécessite-t-il un serveur de nœuds? J'essaye d'obtenir le même style d'URL mais uniquement du côté client. C'est possible?
Sebastialonso
1
non, vous n'avez pas besoin d'un serveur de nœuds. En fait, je fonctionne sur le backend django. Mais vous avez probablement besoin de node pour les outils.
Mox
1
Ok, j'ai essayé cette approche. Quand je frappe F5, tout ce que j'obtiens est "Not found". Est-il possible que cette histoire traite de cela?
Sebastialonso
si u n'est pas trouvé, cela est renvoyé par le serveur. cela signifie que le modèle d'URL ne fait pas partie du routeur de réaction.
Mox
1
Oui, après avoir lu un peu plus, tout est devenu clair. J'ai fini par aller avec hashHistory sans les clés.
Sebastialonso
3

Une autre façon de gérer ce qu'il faut afficher après le hachage (donc si vous n'utilisez pas pushState!) Est de créer votre CustomLocation et de le charger lors de la création de ReactRouter.

Par exemple, si vous souhaitez avoir une URL de hachage (donc avec #!) Pour vous conformer aux spécifications google pour l'exploration, vous pouvez créer un fichier HashbangLocation.js qui copie principalement le HashLocation d'origine tel que:

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

Notez la fonction slashToHashbang .

Alors tu dois juste faire

ReactRouter.create({location: HashbangLocation})

Et c'est tout :-)

Jonathan Banon
la source