J'ai une application côté serveur non-SPA avec React application limitée à la page actuelle, /some/static/page
. L'application a <base href="/">
dans <head>
sur toutes les pages et en dépend, cela ne peut pas être changé.
Voici un exemple de base avec React 16, React Router 4 and <HashRouter>
:
export class App extends React.Component {
render() {
return (
<HashRouter>
<div>
<Route exact path="/" component={Root} />
</div>
</HashRouter>
);
}
}
Tous les itinéraires peuvent être désactivés à des fins de test, mais cela ne change pas le comportement.
Voici create-react-app
project qui montre le problème. Les étapes pour le répliquer sont:
npm i
npm start
http://localhost:3000/some/static/page
HashRouter est clairement affecté par <base>
. Il redirige depuis /some/static/page
à /#/
à l'initialisation, alors que je m'attends à ce que ce soit /some/static/page#/
ou /some/static/page/#/
(fonctionne comme prévu uniquement dans IE 11).
Il y a une éclaboussure rapide du composant Root
avant qu'il ne redirige vers /#/
.
Il redirige vers /foo/#/
en cas de <base href="/foo">
, et il redirige vers /some/static/page/#/
quand <base>
la balise est supprimée.
Le problème affecte Chrome et Firefox (versions récentes) mais pas Internet Explorer (IE 11).
Pourquoi est-ce <HashRouter>
affecté par <base>
? Il est utilisé ici exactement parce qu'il n'est pas censé affecter le chemin de localisation, mais uniquement le hachage.
Comment résoudre ce problème?
En fait, cela à partir de history
. Si vous voyez leur code , ils utilisent simplement createHashHistory
et définissent children
. Donc, cela équivaut à ceci:
import React from 'react';
import { Route, Router } from 'react-router-dom';
import { createHashHistory } from 'history';
const Root = () => <div>Root route</div>;
export default class App extends React.Component {
history = createHashHistory({
basename: "", // The base URL of the app (see below)
hashType: "slash", // The hash type to use (see below)
// A function to use to confirm navigation with the user (see below)
getUserConfirmation: (message, callback) => callback(window.confirm(message)),
});
render() {
return (
<Router history={this.history}>
<div>Router
<Route exact path="/" component={Root} />
</div>
</Router>
);
}
}
Il montrera le même problème que vous. Ensuite, si vous changez le code history
comme ceci:
import {createBrowserHistory } from 'history';
...
history = createBrowserHistory({
basename: "", // The base URL of the app (see below)
forceRefresh: false, // Set true to force full page refreshes
keyLength: 6, // The length of location.key
// A function to use to confirm navigation with the user (see below)
getUserConfirmation: (message, callback) => callback(window.confirm(message))
});
alors votre problème disparaîtra mais n'utilisez certainement pas hash
. Le problème ne vient donc pas de HashRouter
mais de history
.
Parce que cela vient de history
, voyons cela thread . Après avoir lu ce fil, nous pouvons conclure que c'est la fonctionnalité de history
.
donc, si vous définissez <base href="/">
, car vous utilisez hash
(#), lorsque le navigateur est chargé (en fait après componentDidMount
), il ajoutera hash
(#) dans votre cas some/static/page
=> some/static/page
+ /
=> /
+ #/
=> /#/
. Vous pouvez archiver componentDidMount
définir debugger
pour intercepter avant d'ajouter l'itinéraire.
simplement, supprimez simplement l'élément <base href>
ou n'utilisez pas HashRouter
.
Si vous avez toujours besoin, mais que vous souhaitez éviter, de component
spécifique, mettez-le juste avant class
:
const base = document.querySelector("base");
base.setAttribute('href', '');
puisque vous voulez garder la balise base
pour garder le lien persistant et utiliser le routeur hash
, voici la solution proche, je pense.
1. Définissez la balise base
sur vide.
const base = document.querySelector('base');
base.setAttribute('href', '');
placez ce code dans le composant App
(composant d'enveloppement racine) pour appeler une fois.
2. Lorsque componentDidMount
le rétablit
componentDidMount() {
setTimeout(() => {
base.setAttribute('href', '/');
}, 1000);
}
en utilisant le délai d'attente pour attendre réagir fait rendre le dom virtuel.
C'est très proche, je pense (à tester). Parce que vous utilisez le routeur hash
, le lien à partir de l'index html sera sûr (ne pas remplacer par react mais conserver par base
tag). Cela fonctionne aussi avec le lien css <link rel="stylesheet" href="styles.css">
ainsi que.
Si vous voyez https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base#Hint , cela indique que l'utilisation de <base>
même avec les URL #target est un comportement attendu.
Et sur https://reacttraining.com/react-router/web/api/HashRouter il dit dans basename: section de chaîne: Un nom de base correctement formaté devrait avoir une barre oblique, mais pas de barre oblique .
Donc, vous devriez peut-être définir un nom de base différent sur l'élément HashRouter
ou supprimer la barre oblique de fin de <base>
C'est un problème de history
paquet. Il est même résolu, veuillez regarder ce pr
Comme correctif temporaire, je vous suggère de simplement spécifier cette branche dans package.json
"dependencies": {
...
"history": "git://github.com/amuzalevskiy/history.git",
...
}
Et une fois que le correctif sera fusionné dans la branche d'origine - revenez au module npm principal fixe
Concernant le repo: je viens de faire npm run build
sur solution microbouji et résultat validé, car il est impossible d'utiliser le dépôt d'origine sans exécuter le script publish
J'ai terminé avec HOC qui applique simplement un correctif décrit dans cette réponse :
function withBaseFix(HashRouter) {
return class extends React.Component {
constructor() {
super();
this.baseElement = document.querySelector('base');
if (this.baseElement) {
this.baseHref = this.baseElement.getAttribute('href');
this.baseElement.setAttribute('href', '');
}
}
render() {
return <HashRouter {...this.props}>{this.props.children}</HashRouter>;
}
componentDidMount() {
if (this.baseElement)
this.baseElement.setAttribute('href', this.baseHref);
}
}
};
const FixedHashRouter = withBaseFix(HashRouter);
...
<FixedHashRouter>
<div>
<Route exact path="/" component={Root} />
</div>
</FixedHashRouter>
...
Votre observation sur HashRouter
et le <base>
la balise est correcte. J'ai déposé un problème concernant les différences de navigateurs ici: https://github.com/ReactTraining/history/issues/574 et le PR correspondant avec correction ici: https: // github. com/ReactTraining/histoire/pull/577
En attendant, je ne suis pas sûr de tout le routage dont vous avez besoin, mais si l'application react vit entièrement sous /some/static/page/
, vous pouvez probablement le faire fonctionner avec:
<BrowserRouter basename="/some/static/page">
.