如何停止/#/在浏览器与反应路由器?

当使用反应路由器时,有什么方法可以防止 /#/显示在浏览器的地址栏中?那是 ReactJS。例如,点击链接到一个新的路由显示 localhost:3000/#/localhost:3000/#/about取决于路线。

86898 次浏览

If you don't need to support IE8, you can use Browser History and react-router will use window.pushState instead of setting the hash.

How exactly to do this depends on the version of React Router that you are using:

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

For the current version 0.11 and forward, you need to add Router.HistoryLocation to Router.run(). <Routes> are now deprecated. See the Upgrade Guide for the 0.12.x HistoryLocation implementation.

Another way to handle what to display after the hash (so if you don't use pushState !) is to create your CustomLocation and load it at ReactRouter creation.

For exemple, if you want to have hashbang url (so with #!) to comply with google specs for crawling, your can create a HashbangLocation.js file which mainly copy the original HashLocation such as :

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

Note the slashToHashbang function.

Then you juste have to do

ReactRouter.create({location: HashbangLocation})

And that's it :-)

The answer to this question has changed dramatically over the years as React Router has been refactored again and again. Here is a breakdown of how to solve the issue with each version.

Version 6

The idea is to set the router to be a "browser router", which is created using the createBrowserRouter() function. This router is then added to the root element of the React app.

import React from "react";
import ReactDOM from "react-dom/client";
import {
createBrowserRouter,
RouterProvider,
Route,
} from "react-router-dom";


const router = createBrowserRouter([
{
path: "/",
element: ...,
},
]);


ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);

Source react-router Version 6 Docs: createBrowserRouter

Version 4

For version 4 of react-router, the syntax is very different and it is required is to use BrowserRouter as the router root tag.

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

Note that this will work in version 6, but it's not recommended and the BrowserRouter component doesn't support the new React Router data APIs.

Source React Router Version 4 Docs

Versions 2 and 3

For the versions 1, 2 and 3 of React Router, the correct way to set the route to URL mapping scheme is by passing a history implementation into the history parameter of <Router>. From the histories documentation:

In a nutshell, a history knows how to listen to the browser's address bar for changes and parses the URL into a location object that the router can use to match routes and render the correct set of components.

In react-router 2 and 3, your route configuration code will look something like this:

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

Version 1

In version 1.x, you will instead use the following:

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

Source: Version 2.0 Upgrade Guide

You can actually use .htaccess to accomplish this. The browser normally needs the query string delimiter ? or # to determine where the query string begins and the directory paths end. The end result we want is www.mysite.com/dir So we need to catch the issue before the web server searches for the directory it thinks we asked for /dir. So we place a .htaccess file in the root of the project.

    # 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

Then you obtain the query parameters with window.location.pathname

You can then avoid using react routes if you want and just manipulate the url and browser history if you want as well. Hope this helps someone...

Install the history package

npm install history --save

Next import createHistory and useBasename from the history

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

if your app url is www.example.com/myApp, then /root should be /myApp.

pass the history variable to the Router

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

Now for all your Link tags append a "/" in front of all the paths.

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

The inspiration of the solution came from React-Router Example Which unfortunately, was not properly documented in their API.