Hashrouter маршрутизатора React перенаправляет на url-адрес тега

у меня есть серверное приложение без SPA с приложением React, которое ограничено текущей страницей,/some/static/page. приложение <base href="/"> на <head> на всех страницах и полагается на него, это не может быть изменено.

вот основной пример с React 16, React Router 4 и <HashRouter>:

export class App extends React.Component {
    render() {
        return (
            <HashRouter>
                <div>
                    <Route exact path="/" component={Root} />
                </div>
            </HashRouter>
        );
    }
}

все маршруты могут быть отключены для целей тестирования, но это не меняет поведение.

здесь create-react-app проект это показывает проблему. Шаги для его репликации:

  • npm i
  • npm start
  • перейти к http://localhost:3000/some/static/page

HashRouter явно зависит от <base>. Он перенаправляет из /some/static/page to /#/ при инициализации, в то время как я ожидаю, что это будет /some/static/page#/ или /some/static/page/#/ (работает по назначению только в IE 11).

есть быстрый всплеск Root компонент перед перенаправлением на /#/.

это перенаправляет на /foo/#/ в случае <base href="/foo">, и он перенаправляет на /some/static/page/#/, когда <base> тег удаляется.

проблема затрагивает Chrome и Firefox (последние версии), но не Internet Explorer (IE 11).

почему <HashRouter> влияет <base>? Он используется здесь именно потому, что он не должен влиять на путь местоположения, только хэш.

как это можно исправить?

5 ответов


на самом деле это из history. Если вы видите код, они используют только createHashHistory и set children. Так что это эквивалентно этому:

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>
      );
    }
}

он покажет ту же проблему, что и у вас. Тогда, если вы измените history такой код:

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))
});

тогда ваша проблема исчезнет, но определенно не использовать hash. Так что проблема не от HashRouter а от history.

потому что это происходит от history давайте посмотрим этот нить. После прочтения этой темы мы можем сделать вывод, что это характеристика С history.

так, если вы установите <base href="/">, потому что вы используете hash ( # ), когда браузер загружен ( на самом деле после componentDidMount) он будет добавлять hash (#) в вашем случае some/static/page =>some/static/page + / =>/ + #/ =>/#/. Вы можете зарегистрироваться componentDidMount set debugger поймать перед добавлением маршрута.


решение

просто, просто удалите элемент <base href> или не использовать HashRouter.

если еще нужно, но хотите, чтобы избежать определенных component, просто поставьте это перед class:

const base = document.querySelector("base");
base.setAttribute('href', '');

обновление

так как вы хотите сохранить base тег, чтобы сохранить ссылку persist и использовать hash маршрутизатор, вот близкое решение, я думаю.

1. Установить тег base пустое.

const base = document.querySelector('base');
base.setAttribute('href', '');

ставим этот код в App component (root wrap component) для вызова однажды.

2. Когда componentDidMount установить его обратно

componentDidMount() {
  setTimeout(() => {
    base.setAttribute('href', '/');
  }, 1000);
}

используя тайм-аут для ожидания реакции, выполните рендеринг виртуального dom.

это очень близко, я думаю (проверить его). Потому что вы используете hash маршрутизатор, ссылка из индекса html будет безопасной (не переопределять react, но держать base tag). Также он работает с css link <link rel="stylesheet" href="styles.css"> как хорошо.


Если вы видите https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base#Hint, он говорит, что с помощью <base> даже с # target URLs ожидается поведение.

и далее https://reacttraining.com/react-router/web/api/HashRouter он говорит в разделе basename: string: правильно отформатированное базовое имя должно иметь начальную косую черту, но нет Слэша.

возможно, вам следует определить другое базовое имя на HashRouter элемент или удалить косую черту из <base>


Я закончил HOC, который просто применяет исправление, описанное в ответ :

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>
...

Это вопрос history пакета. Он даже разрешен, пожалуйста, посмотрите на это pr

в качестве временного исправления я предлагаю вам просто указать эту ветку в package.json

"dependencies": {
  ...
  "history": "git://github.com/amuzalevskiy/history.git",
  ...
}

и как только исправление будет объединено в исходную ветвь-верните это обратно к фиксированному основному модулю npm


по поводу РЕПО: Я только что сделал npm run build на решение microbouji и commited результат, так как невозможно использовать оригинал репозиторий не работает publish скрипт


ваше замечание о HashRouter и <base> тег является правильным. Я подал вопрос о различиях в браузерах здесь:https://github.com/ReactTraining/history/issues/574 и соответствующий PR с исправлением здесь:https://github.com/ReactTraining/history/pull/577

в то же время, я не уверен обо всех маршрутизации вам нужно, но если приложение react живет полностью под /some/static/page/, вы можете вероятно сделать его работать с:

<BrowserRouter basename="/some/static/page">.