Bagaimana cara menghentikan / # / di browser dengan react-router?

103

Adakah cara untuk mencegah agar tidak /#/muncul di bilah alamat browser saat menggunakan react-router? Itu dengan ReactJS. yaitu Mengklik link untuk pergi ke acara rute baru localhost:3000/#/atau localhost:3000/#/about. Tergantung rutenya.

Rusa Raksasa
sumber
1
Itu karena menggunakan HashHistoryiso BrowserHistory. Lihat juga pertanyaan SO ini di mana saya memberikan banyak info latar belakang tentang hal ini.
Stijn de Witt

Jawaban:

78

Untuk versi 1, 2, dan 3 dari react-router, cara yang benar untuk menyetel rute ke skema pemetaan URL adalah dengan meneruskan implementasi riwayat ke historyparameter <Router>. Dari dokumentasi sejarah :

Singkatnya, riwayat mengetahui cara mendengarkan bilah alamat browser untuk perubahan dan mem-parsing URL menjadi objek lokasi yang dapat digunakan router untuk mencocokkan rute dan merender kumpulan komponen yang benar.

Versi 2 dan 3

Di react-router 2 dan 3, kode konfigurasi rute Anda akan terlihat seperti ini:

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

Versi 1

Di versi 1.x, Anda akan menggunakan yang berikut ini:

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

Sumber: Panduan Peningkatan Versi 2.0

Versi 4

Untuk react-router versi 4 yang akan datang, sintaksnya telah banyak berubah dan yang diperlukan adalah menggunakannya BrowserRoutersebagai tag root router.

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

Sumber React Router Versi 4 Docs

Adam Brown
sumber
6
Perhatikan bahwa historyadalah paket berdiri sendiri Anda harus menginstal.
Jan Klimo
4
Mereka mengubah browserHistorydi v2.x: import { browserHistory } from 'react-router' <Router history={browserHistory} />Periksa panduan peningkatan react-router
pistou
Terima kasih @pistou, saya memperbarui jawaban ke versi 2.0!
Adam Brown
1
Karena hashHistory, apakah ada cara untuk menghilangkan parameter kueri ini di akhir? http://localhost:8080/#/dashboard?_k=yqwtyu
Con Antonakos
2
@Matt Ini berfungsi, tetapi membutuhkan dukungan di server. Itu karena saat Anda menyegarkan, Anda menekan server dengan URL dengan jalur.
Stijn de Witt
40
Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

Untuk versi saat ini 0.11 dan seterusnya, Anda perlu menambahkan Router.HistoryLocationke Router.run(). <Routes>sekarang tidak lagi digunakan. Lihat Panduan Upgrade untuk implementasi HistoryLocation 0.12.x.

pxwise
sumber
1
ini benar-benar merusak aplikasi saya. Sepertinya penerapannya saat ini bermasalah?
ninjaneer
2
@Ninja mungkin memposting pertanyaan baru dengan nomor versi yang tepat untuk react dan react-router, kode gagal dan kesalahan diterima.
pxwise
@Chet Sepertinya dokumen react-router telah diacak. Tautan yang diperbarui ke satu-satunya referensi untuk HistoryLocation yang ditemukan di Panduan Peningkatan.
pxwise
21

Jika Anda tidak perlu mendukung IE8, Anda dapat menggunakan Riwayat Browser dan react-router akan menggunakan window.pushStatealih-alih menyetel hash.

Bagaimana tepatnya melakukan ini tergantung pada versi React Router yang Anda gunakan:

Sophie Alpert
sumber
Terima kasih @ ben-alpert, saya mengerti sekarang.
Rusa Raksasa
1
Saya menambahkan <Routes location="history">semuanya berfungsi dengan baik, sampai Anda menyegarkan browser saat dalam perjalanan yaitu localhost:3000/aboutsaya mendapatkan kesalahan 404. Apakah itu yang diharapkan, saya menggunakan python -m SimpleHTTPServer 3000?
Giant Elk
5
Anda perlu memastikan sisi server Anda dapat menangani url status push. Dalam hal ini mungkin berarti Anda hanya perlu memastikan apa pun yang melayani aplikasi Anda selalu mengirim setiap url yang sampai ke root yang sama. Jadi itu /aboutbenar - benar memuat halaman root Anda /. Jika tidak, server Anda mencoba mencari rute yang cocok /aboutdan tidak menemukan apa pun (404). Saya pribadi tidak menggunakan python tetapi Anda biasanya menemukan rute manual untuk /*atau /.*-> /berfungsi - atau mungkin sesuatu yang disebut html5Modeurl di pengaturan server Anda.
Mike Driver
3
IE9 juga tidak mendukung pushState - jadi itu benar-benar "Jika Anda tidak perlu mendukung IE9", bukan? Saya berharap saya salah.
Cymen
1
Tautan github itu adalah halaman yang tidak ditemukan sekarang.
k00k
9

Anda sebenarnya dapat menggunakan .htaccess untuk melakukannya. Browser biasanya memerlukan pemisah string kueri ?atau #untuk menentukan di mana string kueri dimulai dan jalur direktori berakhir. Hasil akhir yang kami inginkan adalah www.mysite.com/dir Jadi kami perlu menangkap masalah sebelum server web mencari direktori yang menurutnya kami minta /dir. Jadi kami menempatkan .htaccessfile di root proyek.

    # 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

Kemudian Anda mendapatkan parameter kueri dengan window.location.pathname

Anda kemudian dapat menghindari menggunakan rute react jika Anda mau dan hanya memanipulasi url dan riwayat browser jika Anda mau. Semoga ini membantu seseorang ...

Garrett Tacoronte
sumber
Apa yang setara dengan Jboss?
Raghavan
5

Instal paket riwayat

npm install history --save

Selanjutnya impor createHistory dan useBasename dari riwayat

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

jika url aplikasi Anda adalah www.example.com/myApp, maka / root harus / myApp.

meneruskan variabel history ke Router

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

Sekarang untuk semua tag Tautan Anda, tambahkan "/" di depan semua jalur.

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

Inspirasi solusi datang dari Contoh React-Router Yang sayangnya, tidak didokumentasikan dengan baik di API mereka.

Mox
sumber
apakah ini membutuhkan server node? Saya mencoba untuk mencapai gaya URL yang sama tetapi hanya melalui sisi klien. Apa itu mungkin?
Sebastialonso
1
tidak, Anda tidak memerlukan server node. Sebenarnya saya menjalankan django backend. Tapi Anda mungkin membutuhkan node untuk alat.
Mox
1
Oke, saya mencoba pendekatan ini. Saat saya menekan F5, yang saya dapatkan hanyalah "Tidak ditemukan". Mungkinkah sejarah ini berurusan dengan itu?
Sebastialonso
jika Anda tidak ditemukan, itu dikembalikan oleh server. ini berarti pola url bukan bagian dari router react.
Mox
1
Ya, setelah membaca lebih banyak, semuanya menjadi jelas. Saya akhirnya pergi dengan hashHistory tanpa kunci.
Sebastialonso
3

Cara lain untuk menangani apa yang akan ditampilkan setelah hash (jadi jika Anda tidak menggunakan pushState!) Adalah dengan membuat CustomLocation Anda dan memuatnya saat pembuatan ReactRouter.

Misalnya, jika Anda ingin memiliki hashbang url (jadi dengan #!) Untuk mematuhi spesifikasi google untuk perayapan, Anda dapat membuat file HashbangLocation.js yang terutama menyalin HashLocation asli seperti:

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

Perhatikan fungsi slashToHashbang .

Maka Anda harus melakukannya

ReactRouter.create({location: HashbangLocation})

Dan hanya itu :-)

Jonathan Banon
sumber