如何将React应用程序捆绑到服务器上的子目录中?

时间:2016-05-23 16:48:30

标签: reactjs webpack

我有一个我在本地主机上开发的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个文件中的一个或全部)的最佳方式(最好的方法)让它在子目录中工作?

该应用的完整源代码为here以防万一。

谢谢!

10 个答案:

答案 0 :(得分:22)

如果您使用的是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,您还需要在package.json文件中设置"homepage": "/foobar/",。因此,生产构建指向了正确的道路。

答案 1 :(得分: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>
);

您可能还需要在index.html中更新bundle.js文件的路径,如下所示

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

答案 2 :(得分:1)

使用html-webpack-plugin生成最终index.html,并自动将正确的捆绑名称注入其中。

然后在您的webpack配置中设置output.publicPath,告诉webpack您的资产将部署到的子目录:

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

答案 3 :(得分:1)

或者您可以使用环境变量手动调整所有链接。

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目前并不真正支持basename。

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

答案 4 :(得分:1)

除了trenthogan的回复外,我们可以将 basename分配给location.pathname

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

进行此更改后,即使子目录路径将来发生更改,该应用程序也将运行。

答案 5 :(得分:1)

如果要安装在子目录中,则需要在此处指定基本名称。

hash

答案 6 :(得分:0)

在我的情况下,我需要支持一个方案,其中可能有0个到许多名称不明的子文件夹,并且生产仅使用静态文件。

在App.js中,我定义了以下简单函数:

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

我用来定义基本名称:

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

此功能在没有子文件夹的情况下也可以在许多子文件夹下使用,并且还可以管理重新加载。

答案 7 :(得分:0)

  1. 在package.json中添加“首页”:“ https://yourdomain/subdirectory/
  2. 更新路线,在路线中设置子目录,
<Router basename={'/directory-name'}>
  <Route path='/' component={Home} />
</Router>
  1. 在public / index.html中添加基础。这有助于设置没有问题的路径。

    <base href="%PUBLIC_URL%/">

  2. 使所有的css,js,图像和所有资源加载“ ./assets/您的资源”。

  3. 仅查看您是否正在使用历史记录。在历史记录中添加基本名称

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

答案 8 :(得分:0)

谢谢@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 :(得分:-1)

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