使用热重载(webpack-dev-server)的ReactJS服务器端渲染

5
我想尝试使用ReactJS构建我的第一个服务器端渲染。我基于这个Redux教程编写了代码。
当运行npm start (webpack && node server.js)时,应用程序能够正常工作。
我希望使用热重载来快速调试开发环境中的应用程序。因此,我在npm配置中添加了start选项(webpack-dev-server --inline --hot)。请注意,由于react-router正在选择要加载的适当JSX文件,因此我没有index.html文件。
运行npm start会打开我的public文件夹并显示文件,但是不启动我的应用程序。通常我会点击一个index.html文件,但我没有这样的文件。
我不明白发生了什么,并不知道如何解决。谢谢您的帮助。 package.json:
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.prod.js -p",
    "start": "webpack-dev-server --inline --hot",
    "dev": "webpack && node server.js"
  },

webpack.config.js:

module.exports = {
    entry: './client.js',
    output: {
        filename: 'bundle.js',
        path: __dirname + '/public'
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['react', 'es2015', 'stage-0']
                }
            }
        ]
    }
};

server.js

require('babel-register')({
    presets: ['react', 'es2015', 'stage-0']
});

var express = require('express');
var config = require('config');
var app = express();

var serverConfig = config.get('serverConfig');

app.use(express.static('public'));
app.use(require('./routes/index.jsx'));

app.listen(serverConfig.port, function () {
    console.log('FactoryWays server running on port ' + serverConfig.port);
    console.log('Press CTRL-C to stop the server.');
});

client.js

var ReactDOM = require('react-dom');
var React = require('react');
var routes = require('./routes/routes.jsx');
var Redux = require('redux');
var Provider = require('react-redux').Provider;

function reducer(state) { return state; }

var store = Redux.createStore(reducer, window.PROPS);

ReactDOM.render(
    <Provider store={store}>
        {routes}
    </Provider>, document
);

routes/index.jsx:

var router = require('express').Router();
var React = require('react');
var ReactDOMServer = require('react-dom/server');
var ReactRouter = require('react-router');
var Redux = require('redux');
var Provider = require('react-redux').Provider;

function reducer(state) { return state; }

router.get('*', function(request, response) {
    var initialState = { title: 'Universal React' };
    var store = Redux.createStore(reducer, initialState);

    ReactRouter.match({
        routes: require('./routes.jsx'),
        location: request.url
    }, function(error, redirectLocation, renderProps) {
        if (renderProps) {
            var html = ReactDOMServer.renderToString(
                <Provider store={store}>
                    <ReactRouter.RouterContext {...renderProps} />
                </Provider>
            );
            response.send(html);
        } else {
            response.status(404).send('Not Found');
        }
    });
});

module.exports = router;

routes/routes.jsx:

var React = require('react');
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var Route = ReactRouter.Route;
var IndexRoute = ReactRouter.IndexRoute;
var browserHistory = ReactRouter.browserHistory;

module.exports = (
    <Router history={browserHistory}>
        <Route path='/' component={require('../views/Layout.jsx')}>
            <IndexRoute component={require('../views/Index.jsx')} />
        </Route>
    </Router>
);

views/Layout.jsx:

var React = require('react');
var Link = require('react-router').Link;
var connect = require('react-redux').connect;

var Layout = React.createClass({
    _handleClick: function() {
        alert();
    },
    render: function() {
        var custom = this.props.custom;
        return (
            <html>
                <head>
                    <title>{custom.title}</title>
                    <link rel='stylesheet' href='/style.css' />
                </head>
                <body>
                    <h1>{custom.title}</h1>
                    <p>Isn't server-side rendering remarkable?</p>
                    <button onClick={this._handleClick}>Click Me</button>
                    {this.props.children}
                    <ul>
                        <li>
                            <Link to='/'>Home</Link>
                        </li>
                        <li>
                            <Link to='/about'>About</Link>
                        </li>
                    </ul>
                    <script dangerouslySetInnerHTML={{
                        __html: 'window.PROPS=' + JSON.stringify(custom)
                    }} />
                    <script src='/bundle.js' />
                </body>
            </html>
        );
    }
});

var wrapper = connect(
    function(state) {
        return { custom: state };
    }
);

module.exports = wrapper(Layout);
1个回答

3
注意,我没有一个index.html文件,因为react-router会选择正确的JSX文件进行加载。
如果你正在运行webpack-dev-server,仍然需要一个。
Webpack用于将JavaScript捆绑在一起。就是这样。在你的情况下(基于你的webpack.config.js文件),它会在你的public目录中输出一个bundle.js文件。
Webpack Dev Server是一个简单的静态文件服务器,用于帮助开发。它不知道你在server.js中的服务器配置。
你的package.json中的两个命令需要不同的设置:
npm start

npm start 运行 webpack-dev-server。默认情况下,webpack-dev-server 将提供当前目录的内容,所以您需要将 index.html 放在项目的根目录中。 index.html 的内容很简单:只需包含一个链接到 bundle.js 文件的 script 标签的基本 HTML。

npm dev

当您运行 npm dev 时,您正在使用定义在 server.js 中的自己的服务器。在这种情况下,它被配置为为您生成并提供一个 index.html(以及您需要的所有其他静态文件)。

如果您想要进行热重载并运行自己的服务器,则可以将 webpack-dev-middleware 添加到 Express 服务器中。


感谢@bejado的帖子。有几个问题:1. 我理解我的index.html内容应该等于views/Layout.jsx中的内容。然后我可以从那个内容创建一个index.html文件,并在devstart中都提供它。请详细说明。2. 我的最终目标是运行我的服务器并进行热重载。所以,我应该将webpack-dev-server更改为webpack-dev-middleware吗?我应该在生产和开发中都这样做吗(在开发中我不需要)?如何为两种情况配置?如果您能,请详细说明这两种情况。 - Mendes
另外,我看到了webpack-hot-middleware。这与什么相关? - Mendes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接