Como parar / # / no navegador com react-router?

103

Há alguma maneira de evitar que /#/apareça na barra de endereços do navegador ao usar o react-router? Isso é com ReactJS. ou seja, clicar em links para ir para uma nova rota mostralocalhost:3000/#/ ou localhost:3000/#/about. Dependendo da rota.

Alce gigante
fonte
1
É devido ao uso de HashHistoryiso BrowserHistory. Veja também esta questão do SO, onde dou muitas informações básicas sobre este assunto.
Stijn de Witt de

Respostas:

78

Para as versões 1, 2 e 3 do react-router, a maneira correta de definir a rota para o esquema de mapeamento de URL é passando uma implementação de histórico para o historyparâmetro de <Router>. Da documentação de histórias :

Em suma, um histórico sabe como ouvir a barra de endereço do navegador em busca de alterações e analisa a URL em um objeto de localização que o roteador pode usar para combinar rotas e renderizar o conjunto correto de componentes.

Versões 2 e 3

Nos react-router 2 e 3, o seu código de configuração de rota será semelhante a este:

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

Versão 1

Na versão 1.x, você usará o seguinte:

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

Fonte: Guia de atualização da versão 2.0

Versão 4

Para a próxima versão 4 do react-router, a sintaxe mudou muito e é necessário usá-la BrowserRoutercomo a tag raiz do roteador.

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

Documentos do roteador Source React versão 4

Adam Brown
fonte
6
Observe que historyé um pacote independente que você precisa instalar.
Jan Klimo
4
Eles mudaram o browserHistoryin v2.x: import { browserHistory } from 'react-router' <Router history={browserHistory} />Verifique o guia de atualização do
react
Obrigado @pistou, atualizei a resposta para a versão 2.0!
Adam Brown
1
Pois hashHistory, há uma maneira de se livrar desse parâmetro de consulta no final? http://localhost:8080/#/dashboard?_k=yqwtyu
Con Antonakos
2
@Matt Funciona, mas requer suporte no servidor. Isso porque, ao atualizar, você atinge o servidor com uma URL com caminho.
Stijn de Witt
40
Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

Para a versão atual 0.11 e posteriores, você precisa adicionar Router.HistoryLocationa Router.run(). <Routes>agora estão obsoletos. Consulte o Guia de atualização para a implementação de HistoryLocation 0.12.x.

pxwise
fonte
1
isso arruinou completamente meu aplicativo. parece que sua implementação atual está cheia de bugs?
ninjaneer 01 de
2
@Ninja talvez poste uma nova pergunta com os números exatos da versão para react e react-router, código de falha e erros recebidos.
pxwise
@Chet Parece que os documentos do react-router foram embaralhados. Link atualizado para a única referência para HistoryLocation encontrada no Guia de atualização.
pxwise
21

Se você não precisa suportar o IE8, pode usar o histórico do navegador e o react-router usará em window.pushStatevez de definir o hash.

Como exatamente fazer isso depende da versão do Roteador React que você está usando:

Sophie Alpert
fonte
Obrigado @ ben-alpert, entendi agora.
Alce gigante de
1
Eu adicionei <Routes location="history">tudo funciona bem, até que você atualize o navegador quando estiver em rota, ou seja localhost:3000/about, recebo um erro 404. Isso é esperado, estou usando python -m SimpleHTTPServer 3000?
Alce gigante de
5
Você precisa se certificar de que o servidor pode lidar com o URL do estado de envio. Neste caso, provavelmente significa que você só precisa ter certeza de que tudo o que está servindo ao seu aplicativo sempre envia todos os url que obtém para a mesma raiz. Isso /aboutrealmente carrega sua página raiz /. Caso contrário, seu servidor está tentando procurar uma rota que corresponda /aboute não encontra nada (404). Eu pessoalmente não uso o python, mas geralmente você encontra uma rota manual para /*ou /.*-> /funciona - ou pode ser algo chamado html5Modeurls nas configurações do servidor.
Mike Driver
3
O IE9 também não oferece suporte a pushState - isso é realmente "Se você não precisa oferecer suporte ao IE9", certo? Eu gostaria de estar errado.
Cymen 01 de
1
Esse link do github é uma página que não foi encontrada agora.
k00k de
9

Você pode usar .htaccess para fazer isso. O navegador normalmente precisa do delimitador de string de consulta ?ou #para determinar onde a string de consulta começa e os caminhos do diretório terminam. O resultado final que queremos é www.mysite.com/dir Portanto, precisamos detectar o problema antes que o servidor da web procure o diretório que ele pensa que pedimos /dir. Então, colocamos um .htaccessarquivo na raiz do projeto.

    # 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

Em seguida, você obtém os parâmetros de consulta com window.location.pathname

Você pode então evitar o uso de rotas de reação, se quiser, e apenas manipular o url e o histórico do navegador, se quiser. Espero que isso ajude alguém ...

Garrett Tacoronte
fonte
Qual é o equivalente para Jboss?
Raghavan
5

Instale o pacote de histórico

npm install history --save

Em seguida, importe createHistory e useBasename do histórico

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

se o URL do seu aplicativo for www.example.com/myApp, / root deve ser / myApp.

passe a variável de histórico para o roteador

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

Agora, para todas as suas tags de Link, acrescente um "/" na frente de todos os caminhos.

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

A inspiração da solução veio do React-Router Example que, infelizmente, não foi devidamente documentado em sua API.

Mox
fonte
isso requer um servidor de nó? Estou tentando obter o mesmo estilo de URL, mas apenas pelo lado do cliente. É possível?
Sebastialonso
1
não, você não precisa de um servidor de nó. Na verdade, estou executando no backend django. Mas você provavelmente precisa de um nó para ferramentas.
Mox de
1
Ok, eu tentei essa abordagem. Quando pressiono F5, tudo que recebo é "Não encontrado". É possível essa história lidar com isso?
Sebastialonso
se vc não for encontrado, isso é retornado pelo servidor. isso significa que o padrão de url não faz parte do roteador react.
Mox de
1
Sim, depois de ler um pouco mais, tudo ficou claro. Acabei indo com o hashHistory sem as chaves.
Sebastialonso
3

Outra maneira de lidar com o que exibir após o hash (portanto, se você não usar pushState!), É criar seu CustomLocation e carregá-lo na criação do ReactRouter.

Por exemplo, se você deseja ter um url hashbang (portanto, com #!) Para obedecer às especificações do Google para rastreamento, você pode criar um arquivo HashbangLocation.js que copia principalmente o 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;

Observe a função slashToHashbang .

Então você tem que fazer

ReactRouter.create({location: HashbangLocation})

E é isso :-)

Jonathan Banon
fonte