【发布时间】:2016-05-03 17:24:03
【问题描述】:
最近我一直在涉足 React 和 Webpack,到目前为止我一直很喜欢它。当然,这需要大量阅读、查看示例并尝试一些东西,但最终,反应本身以及热重新加载我的组件对我来说越来越重要,我很确定我想继续这种方式。
在过去的几天里,尽管我一直在尝试让我的非常简单的“应用程序”(到目前为止,它只是一个带有几个子组件和反应路由器以显示虚拟页面的菜单)呈现服务器端。
这是我的项目布局:
到目前为止,我的 webpack 配置如下所示:
let path = require("path"),
webpack = require("webpack"),
autoprefixer = require("autoprefixer");
module.exports = {
entry: [
"webpack-hot-middleware/client",
"./client"
],
output: {
path: path.join(__dirname, "dist"),
filename: "bundle.js",
publicPath: "/static/"
},
resolve: {
root: path.resolve(__dirname, "common")
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
exclude: /node_modules/,
include: __dirname
},
{ test: /\.scss$/, loader: "style!css!sass!postcss" },
{ test: /\.svg$/, loader: "file" }
]
},
sassLoader: {
includePaths: [path.resolve(__dirname, "common", "scss")]
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
BROWSER: JSON.stringify(true),
NODE_ENV: JSON.stringify( process.env.NODE_ENV || "development" )
}
}),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
postcss: [autoprefixer({remove: false})]
}
而且,省略模块要求,我的服务器:
// Initialize express server
const server = express();
const compiler = webpack(webpackConfig);
server.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: webpackConfig.output.publicPath }));
server.use(webpackHotMiddleware(compiler));
// Get the request
server.use((req, res) => {
// Use React Router to match our request with our components
const location = createLocation(req.url);
match({routes, location}, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
res.send(renderFullPage(renderProps));
} else {
res.status(404).send("Not found");
}
})
});
function renderFullPage(renderProps) {
let html = renderToString(<RoutingContext {...renderProps}/>);
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">${html}</div>
<script src="/static/bundle.js"></script>
</body>
</html>
`;
}
server.listen(3000);
这里有一点 React Router 服务器端的特殊性,但我可以理解它。
然后出现了在组件内需要样式的问题。过了一会儿,我想通了这一点(感谢 github 问题线程):
if (process.env.BROWSER) {
require("../scss/components/menu.scss");
logo = require("../images/flowychart-logo-w.svg") ;
}
除此之外,我仍然需要在我的客户端上声明一个路由器,重新使用我的服务器已经使用的路由:
// Need to implement the router on the client too
import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router";
import createBrowserHistory from "history/lib/createBrowserHistory";
import routes from "../common/Routes";
const history = createBrowserHistory();
ReactDOM.render(
<Router children={routes} history={history} />,
document.getElementById("app")
);
现在我来了。我的风格得到应用,一切正常。我仍然不是专家,但到目前为止我有点了解正在发生的一切(这是我不使用现有的通用 js / react 样板的主要原因 - 实际上了解正在发生的事情)。
但是现在我要解决的下一个问题是如何在我的组件中使用我的图像。 有了这个设置,它确实可以工作,但是我收到了这个警告:
Warning: React attempted to reuse markup in a container but the
checksum was invalid.
This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting.
React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) "><img class="logo" src="/static/31d8270
(server) "><img class="logo" alt=""
这是有道理的,正如您在之前的代码 sn-p 中所看到的,我实际上在浏览器环境中设置了一个 logo 变量,并使用该变量来设置我的组件的 src 标记。果然在服务器上,然后没有给src属性,所以报错。
现在我的问题: 我做的风格/图像要求对吗?为了摆脱那个警告,我错过了什么?
我喜欢 webpack/react 关联的一件事是我如何使这些依赖关系接近使用它们的 UI 的实际部分。然而,我发现的一件事在大多数教程中都被忽略了,那就是如何在渲染服务器端时保持这种甜蜜的工作。 如果你们能给我一些见解,我将非常感激。
非常感谢!
【问题讨论】:
标签: javascript reactjs webpack server-side universal