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

Posted

技术标签:

【中文标题】带有热重载的 ReactJS 服务器端渲染 (webpack-dev-server)【英文标题】:ReactJS server side rendering with hot reloading (webpack-dev-server) 【发布时间】:2017-07-06 10:05:28 【问题描述】:

我正在尝试使用 ReactJS 构建我的第一个服务器端渲染。我的代码基于this Redux tutorial

应用程序在运行npm start (webpack && node server.js) 时工作​​正常。

我想使用热重载来在开发环境中进行应用程序的快速调试。所以我在我的 npm 配置 (webpack-dev-server --inline --hot) 中添加了 start 选项。请注意,我没有 index.html 文件,因为 react-router 正在选择要加载的正确 JSX 文件。

npm start 打开我的公共文件夹并显示文件,但不启动我的应用程序。通常我会点击 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】:

请注意,我没有 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 的内容很简单:只是样板 HTML,其中包含链接到您的 bundle.js 文件的 script 标记。

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 吗?我应该在生产和开发中都这样做吗(我不需要它)?如何为这两种情况进行配置?如果可以的话,请详细说明这两个条件。 另外,我见过 webpack-hot-middleware。这与什么有关?

以上是关于带有热重载的 ReactJS 服务器端渲染 (webpack-dev-server)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ReactJS Docker 中热重载

尽管我已经完成了“内联:真”,但 Webpack 开发服务器热重载不能与 reactjs 一起使用?

Django、ReactJS、Webpack 热重载

在 docker 应用程序中启用 webpack 热重载

blazor 服务器端是不是有任何热重载?

发布后 ReactJs.net 服务器端渲染失败