Как связать приложение React с подкаталогом на сервере?

У меня есть приложение React, которое я разрабатывал на своем локальном хосте. Я хочу скопировать его на сервер в подкаталог под названием vensa.

Мой конфигурационный файл webpack выглядит так.

const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  entry: [
    './src/index.js'
  ],
  output: {
    path: 'build',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel'
      },
      {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract('style', 'css!sass')
      },
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract('style', 'css')
      },
      {
        test: /\.(png|eot|svg|ttf|woff(2)?)(\?v=\d+\.\d+\.\d+)?/,
        loader: 'url'
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin('vensa-dashboard.css')
  ],
  devServer: {
    historyApiFallback: true,
    contentBase: './build'
  }
};

Файл index.html выглядит так:

<!DOCTYPE html>
<html>
<head>
  <title>Vensa Development Test</title>
  <link rel="stylesheet" href="/vensa-dashboard.css">
</head>
<body>
  <div class="container"></div>
  <script src="/bundle.js"></script>
</body>
</html>

и мой файл routes.js...

import React from 'react';
import { Route, IndexRoute } from 'react-router';
import VensaDashboard from './components/VensaDashboard';
import Inbox from './components/Inbox';
import Todo from './components/Todo';
import Home from './components/Home';

export default (
  <Route path="/" component={VensaDashboard}>
    <IndexRoute component={Home} />
    <Route path="/message" component={Inbox} />
    <Route path="/todo/:todoPage" component={Todo} />
  </Route>
);

Однако, если я просто запустил webpack -p и скопировал эти 3 файла в этот подкаталог, он не работает, так как корневой путь / и не может найти файлы js и css. Я не уверен, что (лучший способ) изменить (возможно, один или все эти 3 файла), чтобы заставить его работать в подкаталоге?

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

Спасибо!

Ответ 1

Если вы используете React Router v4, вы можете установить его с помощью basename = { foobar}.

<Router history={browserHistory} basename={'foobar'}>
  <Route path="/" component={App} />
</Router>

Ссылка на документы: https://reacttraining.com/react-router/web/api/BrowserRouter

Примечание: если вы используете create-react-app в подкаталоге, вы также захотите установить "homepage": "/foobar/", в файле package.json. Таким образом, производственные сборки указывают на правильный путь.

Ответ 2

Мне пришлось сделать что-то подобное недавно, чтобы получить приложение для реагирования, запущенное в подкаталоге. Попробуйте ниже и посмотрите, как вы это делаете.

import React from 'react';
import { Router, Route, IndexRoute, useRouterHistory  } from 'react-router';
import { createHistory } from 'history';
import VensaDashboard from './components/VensaDashboard';
import Inbox from './components/Inbox';
import Todo from './components/Todo';
import Home from './components/Home';

// specify basename below if running in a subdirectory or set as "/" if app runs in root
const appHistory = useRouterHistory(createHistory)({
  basename: "/vensa"
});

export default (
  <Router history={appHistory} />
    <Route path="/" component={VensaDashboard}>
      <IndexRoute component={Home} />
      <Route path="/message" component={Inbox} />
      <Route path="/todo/:todoPage" component={Todo} />
    </Route>
  </Router>
);

Вам также может потребоваться обновить путь к вашему файлу bundle.js в index.html, как показано ниже

<!DOCTYPE html>
<html>
<head>
  <title>Vensa Development Test</title>
  <link rel="stylesheet" href="/vensa-dashboard.css">
</head>
<body>
  <div class="container"></div>
  <script src="bundle.js"></script>
</body>
</html>

Ответ 3

  1. Добавьте "homepage": " https://yourdomain/subdirectory/ " в package.json
  2. Обновите маршруты, установите подкаталог в маршруте,
<Router basename={'/directory-name'}>
  <Route path='/' component={Home} />
</Router>
  1. Добавьте базу в public/index.html. Это помогает установить путь без проблем.

    <base href="%PUBLIC_URL%/">

  2. сделать все CSS, JS, изображения и все ресурсы загрузки "./assets/your resource."

  3. Только посмотрите, если вы используете историю. добавить базовое имя в историю

    const historyConfig = {
        basename: 'your subdirectory'
    };
    const history = createBrowserHistory(historyConfig);

Ответ 4

Используйте html-webpack-plugin, чтобы сгенерировать окончательный index.html с правильными именами пакетов, которые были введены в него.

Затем установите output.publicPath в свою конфигурацию webpack, чтобы сообщить webpack о том, что ваши ресурсы будут развернуты:

output: {
  path: 'build',
  publicPath: "/vensa/",
  filename: 'bundle.js'
},

Ответ 5

Или вы можете использовать переменную окружения, чтобы настроить все ваши ссылки вручную.

const ENV = process.env.NODE_ENV || 'local'; //development
const config = {
    publicPath: ENV !== 'production' ? '/' : '/dev/'
};
plugins: ([
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(ENV),
            'process.env.config': JSON.stringify(config)
        })
})

Тогда один из ваших маршрутов:

<Route path={process.env.config.publicPath + "account/"} component={Account} />

Тогда ваши ссылки:

<Link class="panel-close" href={process.env.config.publicPath + "account/"} >Account</Link>

Это отлично сработало для меня. Тем более, что я использую preact, чей preact-router не поддерживает базовое имя в настоящее время.

<Router history={browserHistory} basename={'foobar'}>
  <Route path="/" component={App} />
</Router>

Ответ 6

Добавляя к ответу trenthogan, мы можем присвоить basename для location.pathname

const loc = window.location || {};
<Router history={browserHistory} basename={loc.pathname || 'foobar'}>
  <Route path="/" component={App} />
</Router>

С этим изменением приложение будет работать, даже если путь к подкаталогу изменится в будущем.

Ответ 7

В моей ситуации мне нужно было поддерживать сценарий, в котором может быть от 0 до многих подпапок с неизвестными именами, а в производстве используются только статические файлы.

В моем App.js я определил эту простую функцию:

const getBasename = path => path.substr(0, path.lastIndexOf('/'));

Который я использую для определения базового имени:

<Router basename={getBasename(window.location.pathname)}>

Это работает как без подпапок, так и со многими подпапками, а также может управлять перезагрузками.

Ответ 8

Спасибо @trenthogan, это круто. Но, будучи новичком, я был озадачен тем, куда на самом деле поместить код, поскольку я работал из учебника без ROUTER. Вот что сработало для меня, как только я все выяснил...

в моем index.js...

import React from 'react';
import ReactDOM from 'react-dom';
import './css/index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter as Router, Route } from 'react-router-dom';

ReactDOM.render(
    <Router basename={'foobar'}>
        <Route path="/" component={App} />
    </Router>, document.getElementById('root'));
serviceWorker.unregister();

Который ранее выглядел как...

import React from 'react';
import ReactDOM from 'react-dom';
import './css/index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();

Ответ 9

var sourcePath = path.resolve(__dirname, 'src', 'index.js');
var buildPath = path.resolve(__dirname, 'vensa');

module.exports = {
    entry: [sourcePath],
    output: {
        path: buildPath,
        filename: '[name]-[hash].js',
        hash: true
    }
    ...