【问题标题】:Universal React.JS - Requiring Styles and ImagesUniversal React.JS - 需要样式和图像
【发布时间】: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


    【解决方案1】:

    我会在这里推荐 webpack-isomorphic-tools 作为解决方案。

    我在示例项目中有一节介绍如何将 SVG 导入 React 应用程序:https://github.com/peter-mouland/react-lego#importing-svgs

    这是将我的 Universal React 应用程序转换为接受 SVG 的应用程序所需的代码:https://github.com/peter-mouland/react-lego/compare/svg

    【讨论】:

      【解决方案2】:

      您至少可以采取两种方法:

      1. 尝试对代码进行 de-webpackify 以在 Node 中运行。
      2. 使用 target 'node' 使用 Webpack 编译您​​的客户端代码。

      第一个选项要求您删除 Webpack 特定代码,例如 require.ensure / System.import 并教节点您的模块解析规则以及如何将非 js 资产转换为 js。这可以通过一些 babel 转换和可能对 NODE_PATH 的修改来实现,但是这种方法确实意味着你一直在追逐你的尾巴,即如果你开始使用新的 Webpack 功能/加载器/插件,你需要确保你是能够支持 N​​ode 中的等效功能。

      第二种方法可能更实用,因为您可以确定您使用的任何 Webpack 功能也可以在服务器上运行。这是您可以做到的一种方式:

      // webpack.config.js
      module.exports = [
          {
              target: 'web',
              entry: './client',
              output: {
                  path: dist,
                  filename: 'client.js'
              }
          }, {
              target: 'node',
              entry: './server',
              output: {
                  path: dist,
                  filename: 'server.js',
                  libraryTarget: 'commonjs2'
              }
          }
      ];
      

      // client.js
      renderToDom(MyApp, document.body);
      

      // server.js
      module.exports = (req, res, next) => {
          res.send(`
              <!doctype html>
              <html>
                  <body>
                      ${renderToString(MyApp)}`
                      <script src="/dist/client.js"></script>
                  </body>
              </html>
          `);
      }
      

      // index.js
      const serverRenderer = require('./dist/server');
      server.use(serverRenderer);
      server.listen(6060);
      

      【讨论】:

        猜你喜欢
        • 2017-06-08
        • 1970-01-01
        • 1970-01-01
        • 2019-01-25
        • 1970-01-01
        • 1970-01-01
        • 2016-11-18
        • 2019-07-03
        • 2020-12-08
        相关资源
        最近更新 更多