จะหยุด / # / ในเบราว์เซอร์ด้วย react-router ได้อย่างไร?


103

มีวิธีใดในการป้องกันไม่ให้/#/แสดงในแถบที่อยู่ของเบราว์เซอร์เมื่อใช้ react-router? นั่นคือด้วย ReactJS เช่นการคลิกลิงก์ที่จะไปแสดงให้เห็นเส้นทางใหม่หรือlocalhost:3000/#/ localhost:3000/#/aboutขึ้นอยู่กับเส้นทาง


1
มันเกิดจากการใช้มาตรฐาน ISOHashHistory BrowserHistoryดูคำถาม SO นี้ที่ฉันให้ข้อมูลพื้นฐานมากมายเกี่ยวกับเรื่องนี้
Stijn de Witt

คำตอบ:


78

สำหรับรุ่นที่ 1, 2 และ 3 ของการตอบสนองเตอร์, วิธีที่ถูกต้องในการตั้งค่าเส้นทางไปโครงการทำแผนที่ URL ที่เป็นโดยผ่านการดำเนินงานประวัติศาสตร์เข้าไปในพารามิเตอร์ของhistory <Router>จากเอกสารประวัติ :

โดยสรุปประวัติจะรู้วิธีฟังแถบที่อยู่ของเบราว์เซอร์สำหรับการเปลี่ยนแปลงและแยกวิเคราะห์ URL เป็นวัตถุตำแหน่งที่เราเตอร์สามารถใช้เพื่อจับคู่เส้นทางและแสดงชุดส่วนประกอบที่ถูกต้อง

เวอร์ชัน 2 และ 3

ใน react-router 2 และ 3 รหัสการกำหนดเส้นทางของคุณจะมีลักษณะดังนี้:

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

เวอร์ชัน 1

ในเวอร์ชัน 1.x คุณจะใช้สิ่งต่อไปนี้แทน:

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

ที่มา: คู่มือการอัปเกรดเวอร์ชัน 2.0

เวอร์ชัน 4

สำหรับ react-router เวอร์ชัน 4 ที่กำลังจะมานั้นไวยากรณ์ได้เปลี่ยนไปอย่างมากและจำเป็นต้องใช้BrowserRouterเป็นแท็กรูทของเราเตอร์

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

Source React Router เวอร์ชัน 4 เอกสาร


6
โปรดทราบว่าhistoryเป็นแพ็คเกจแบบสแตนด์อะโลนที่คุณจะต้องติดตั้ง
ม.ค. Klimo

4
พวกเขาเปลี่ยนbrowserHistoryใน v2.x: import { browserHistory } from 'react-router' <Router history={browserHistory} />ตรวจสอบคู่มือการอัปเกรด react-router
pistou

ขอบคุณ @pistou ฉันอัปเดตคำตอบเป็นเวอร์ชัน 2.0 แล้ว!
Adam Brown

1
สำหรับhashHistoryมีวิธีกำจัดพารามิเตอร์แบบสอบถามนี้ในตอนท้ายหรือไม่? http://localhost:8080/#/dashboard?_k=yqwtyu
Con Antonakos

2
@Matt มันใช้งานได้ แต่ต้องการการสนับสนุนบนเซิร์ฟเวอร์ นั่นเป็นเพราะเมื่อคุณรีเฟรชคุณเข้าสู่เซิร์ฟเวอร์ด้วย URL ที่มีเส้นทาง
Stijn de Witt

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

สำหรับเวอร์ชันปัจจุบัน 0.11 และไปข้างหน้าคุณจะต้องเพิ่มการRouter.HistoryLocation เลิกใช้งานแล้ว ดูคู่มือการอัพเกรดสำหรับการใช้งาน HistoryLocation 0.12.xRouter.run()<Routes>


1
สิ่งนี้ทำลายแอปของฉันอย่างสิ้นเชิง ดูเหมือนว่าการใช้งานในปัจจุบันของพวกเขามีข้อผิดพลาด?
ninjaneer

2
@Ninja อาจโพสต์คำถามใหม่พร้อมหมายเลขเวอร์ชันที่แน่นอนสำหรับ react และ react-router รหัสที่ล้มเหลวและข้อผิดพลาดที่ได้รับ
pxwise

@Chet ดูเหมือนว่าเอกสารปฏิกิริยาของเราเตอร์จะถูกสับ อัปเดตลิงก์ไปยังข้อมูลอ้างอิงเดียวสำหรับ HistoryLocation ที่พบในคู่มือการอัปเกรด
pxwise

21

หากคุณไม่ต้องการรองรับ IE8 คุณสามารถใช้ประวัติเบราว์เซอร์และเราเตอร์ตอบสนองจะใช้ window.pushStateแทนการตั้งค่าแฮช

วิธีการทำสิ่งนี้ขึ้นอยู่กับรุ่นของ React Router ที่คุณใช้:


ขอบคุณ @ ben-alpert ฉันเข้าใจแล้ว
Giant Elk

1
ฉันเพิ่ม<Routes location="history">ทุกอย่างทำงานได้ดีจนกว่าคุณจะรีเฟรชเบราว์เซอร์เมื่ออยู่บนเส้นทางเช่นlocalhost:3000/aboutนั้นฉันจะได้รับข้อผิดพลาด 404 คาดว่าฉันกำลังใช้อยู่python -m SimpleHTTPServer 3000?
Giant Elk

5
คุณต้องตรวจสอบให้แน่ใจว่าเซิร์ฟเวอร์ของคุณสามารถจัดการกับ URL สถานะพุชได้ ในกรณีนี้อาจหมายความว่าคุณเพียงแค่ต้องตรวจสอบให้แน่ใจว่าสิ่งที่ให้บริการแอปของคุณจะส่งทุก url ที่ไปยังรูทเดียวกันเสมอ เพื่อให้/aboutโหลดหน้ารูทของคุณได้/จริง มิฉะนั้นเซิร์ฟเวอร์ของคุณจะพยายามค้นหาเส้นทางที่ตรงกัน/aboutและไม่พบอะไรเลย (404) ฉันไม่ได้ใช้ python เป็นการส่วนตัว แต่คุณมักจะพบเส้นทางด้วยตนเองสำหรับ/*หรือ/.*-> ใช้/งานได้ - หรืออาจเป็นสิ่งที่เรียกว่าhtml5ModeURL ในการตั้งค่าเซิร์ฟเวอร์ของคุณ
Mike Driver

3
IE9 ไม่รองรับ pushState ด้วย - นั่นคือ "ถ้าคุณไม่จำเป็นต้องรองรับ IE9" ใช่มั้ย? ฉันหวังว่าฉันจะผิด
Cymen

1
ลิงก์ github นั้นเป็นหน้าที่ไม่พบในขณะนี้
k00k

9

คุณสามารถใช้. htaccess เพื่อทำสิ่งนี้ได้ โดยปกติเบราว์เซอร์ต้องการตัวคั่นสตริงคิวรี?หรือ#กำหนดตำแหน่งที่สตริงเคียวรีเริ่มต้นและพา ธ ไดเร็กทอรีสิ้นสุด ผลลัพธ์ที่เราต้องการคือดังนั้นเราจึงจำเป็นที่จะจับปัญหาก่อนที่การค้นหาเว็บเซิร์ฟเวอร์สำหรับไดเรกทอรีมันคิดว่าเราถามหาwww.mysite.com/dir /dirดังนั้นเราจึงวาง.htaccessไฟล์ไว้ในรูทของโปรเจ็กต์

    # 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

จากนั้นคุณจะได้รับพารามิเตอร์เคียวรีด้วย window.location.pathname

จากนั้นคุณสามารถหลีกเลี่ยงการใช้เส้นทางการตอบสนองได้หากคุณต้องการและเพียงแค่ปรับเปลี่ยน url และประวัติเบราว์เซอร์หากคุณต้องการเช่นกัน หวังว่านี่จะช่วยใครสักคน ...


อะไรที่เทียบเท่ากับ Jboss?
Raghavan

5

ติดตั้งแพ็คเกจประวัติ

npm install history --save

นำเข้า createHistory และ useBasename ถัดไปจากประวัติ

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

หาก url แอปของคุณคือ www.example.com/myApp ดังนั้น / root ควรเป็น / myApp

ส่งผ่านตัวแปรประวัติไปยังเราเตอร์

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

ตอนนี้สำหรับแท็กลิงก์ทั้งหมดของคุณจะต่อท้าย "/" ไว้หน้าเส้นทางทั้งหมด

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

แรงบันดาลใจของโซลูชันมาจากReact-Router Example ซึ่งน่าเสียดายที่ไม่ได้รับการบันทึกไว้อย่างถูกต้องใน API


สิ่งนี้ต้องการเซิร์ฟเวอร์โหนดหรือไม่ ฉันกำลังพยายามใช้รูปแบบ URL เดียวกัน แต่ผ่านทางฝั่งไคลเอ็นต์เท่านั้น เป็นไปได้ไหม?
Sebastialonso

1
ไม่คุณไม่จำเป็นต้องมีเซิร์ฟเวอร์โหนด อันที่จริงฉันทำงานบนแบ็กเอนด์ django แต่คุณอาจต้องการโหนดสำหรับเครื่องมือ
Mox

1
โอเคฉันลองวิธีนี้แล้ว เมื่อฉันกด F5 สิ่งที่ฉันได้รับคือ "ไม่พบ" เป็นไปได้ไหมที่ประวัติศาสตร์นี้จะจัดการกับสิ่งนั้น?
Sebastialonso

หากคุณไม่พบสิ่งนั้นจะถูกส่งกลับโดยเซิร์ฟเวอร์ ซึ่งหมายความว่ารูปแบบ url ไม่ได้เป็นส่วนหนึ่งของ react router
Mox

1
ใช่หลังจากอ่านอีกเล็กน้อยทุกอย่างก็ชัดเจน ฉันจบลงด้วย hashHistory โดยไม่ต้องใช้กุญแจ
Sebastialonso

3

อีกวิธีหนึ่งในการจัดการสิ่งที่จะแสดงหลังจากแฮช (ดังนั้นหากคุณไม่ได้ใช้ pushState!) คือการสร้าง CustomLocation ของคุณและโหลดที่การสร้าง ReactRouter

ตัวอย่างเช่นหากคุณต้องการมี hashbang url (ดังนั้นจึงมี #!) เพื่อให้สอดคล้องกับข้อกำหนดของ Google สำหรับการรวบรวมข้อมูลคุณสามารถสร้างไฟล์ HashbangLocation.js ซึ่งส่วนใหญ่จะคัดลอก HashLocation ดั้งเดิมเช่น:

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

สังเกตฟังก์ชันslashToHashbang

จากนั้นคุณก็ต้องทำ

ReactRouter.create({location: HashbangLocation})

และนั่นแหล่ะ :-)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.