【问题标题】:Conflicting server/client rendering and Webpack’s local css modules冲突的服务器/客户端渲染和 Webpack 的本地 css 模块
【发布时间】:2015-12-16 19:25:03
【问题描述】:

我正在使用 Fluxible 来帮助在一个新项目上创建一个同构应用程序,并且它运行良好。到目前为止我喜欢它。不过,我遇到了减速带,想知道如何克服它。

到目前为止,这是我的 Header 组件:

import React from 'react'
import Nav from '../Nav/Nav'

import classNames from 'classnames'
if (process.env.BROWSER) var styles = require('./Header.css')

class Header extends React.Component {
    render() {

        // Header classes
        var theClasses = process.env.BROWSER ? classNames({
            [styles.Header]: true
        }) : ''

        return (
            <header className={theClasses}>
                <Nav selected={this.props.selected} links={this.props.links} />
            </header>
        )
    }
}

export default Header

你会看到我正在使用process.env.BROWSER 来检测我使用的是哪个 ENV。如果我们在客户端,我需要 CSS。如果我们在服务器上,我会跳过它。效果很好。

问题出现在文件的后面,我根据 Header.css 文件的内容构建 theClasses 对象,然后像这样在 Header 上使用这些类:

<header className={theClasses}>
    <Nav selected={this.props.selected} links={this.props.links} />
</header>

问题是因为我没有在服务器上加载 css,theClasses 最终为空,并且为客户端呈现的内容最终与服务器上的内容不同。 React 显示此警告:

警告:React 试图在容器中重用标记,但是 校验和无效。这通常意味着您正在使用服务器 渲染和服务器上生成的标记不是 客户期待。 React 注入了新的标记来弥补 可以,但是您已经失去了服务器渲染的许多好处。 相反,要弄清楚为什么生成的标记在 客户端或服务器:

(client) n28"&gt;&lt;header class="Header--Header_ip_OK

(server) n28"&gt;&lt;header class="" data-reactid=".2ei

你会建议什么来解决这个问题?


2015 年 9 月 24 日更新

最初的问题是我无法在服务器端编译 CSS,所以我开始像这样检查 BROWSER:

if (process.env.BROWSER) var styles = require('./Application.css')

如果我删除 if (process.env.BROWSER) 位,我会收到此错误:

SyntaxError: src/components/Application/Application.css: Unexpected token (2:0)
  1 |
> 2 | @import 'styles/index.css';
    | ^
  3 |

在以下简单的 CSS 文件中:

@import 'styles/index.css';

.Application {
    box-shadow: 0 0 0 1px var(--medium-gray);
    box-sizing: border-box;
    lost-center: 1080px 32px;
}

我从Fluxible Yo Generator 开始这个项目,它在这里提供了两个 Webpack 配置文件:https://github.com/yahoo/generator-fluxible/tree/master/app/templates

我用几个加载器更新了我的:

var webpack = require('webpack');
var path = require('path');

module.exports = {
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    entry: [
        'webpack-dev-server/client?http://localhost:3000',
        'webpack/hot/only-dev-server',
        './client.js'
    ],
    output: {
        path: path.resolve('./build/js'),
        publicPath: '/public/js/',
        filename: 'main.js'
    },
    module: {
        loaders: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                loaders: [
                    require.resolve('react-hot-loader'),
                    require.resolve('babel-loader')
                ]
            }, {
                test: /\.css$/,
                loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]_[hash:base64:5]!postcss-loader'
            }, {
                test: /\.(png|jpg|svg)$/,
                loader: 'url?limit=25000'
            }, {
                test: /\.json$/,
                loader: 'json-loader'
            }
        ]
    },
    postcss: function () {
        return [
            require('lost'),
            require('postcss-import')({
                path: ['./src/'],
                onImport: function (files) {
                    files.forEach(this.addDependency);
                }.bind(this)
            }),
            require('postcss-mixins'),
            require('postcss-custom-properties'),
            require('autoprefixer')({
                browsers: ['last 3 versions']
            })
        ];
    },
    node: {
        setImmediate: false
    },
    plugins: [
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin(),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: JSON.stringify(process.env.NODE_ENV),
                BROWSER: JSON.stringify(true)
            }
        })
    ],
    devtool: 'eval'
};

这就是我所在的地方……不知道如何在服务器端编译 CSS。感谢我能得到的任何帮助。

【问题讨论】:

    标签: javascript reactjs webpack fluxible


    【解决方案1】:

    你可以试试css-modules-require-hook

    webpack.config.js

    {
      test: /\.css$/,
      loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]___[hash:base64:5]'
    }
    

    server.js

    require('css-modules-require-hook')({
      // This path should match the localIdentName in your webpack css-loader config.
      generateScopedName: '[name]__[local]___[hash:base64:5]'
    })
    

    【讨论】:

      【解决方案2】:

      您需要确保服务器生成与客户端相同的标记,这意味着您也需要在服务器上使用 CSS 模块。

      在我当前的项目中,我们也通过使用 Webpack 编译我们的 Node 代码来实现这一点。 James Long 写了一篇关于如何设置的非常好的指南,分布在三篇博客文章中:

      Backend Apps with Webpack (Part I)

      Backend Apps with Webpack (Part II)

      Backend Apps with Webpack (Part III)

      【讨论】:

        【解决方案3】:

        您当然应该使用类来渲染服务器端 HTML,否则您将错过时间来享受同构应用程序的好处。这意味着您仍然必须等待 JS 加载才能应用 CSS 样式,这违背了构建 HTML 服务器端的某些目的。这也意味着您不能“cut the mustard”并为旧浏览器提供关闭 Javascript 的应用程序。

        问题是你为什么不在服务器上渲染 CSS 类?这也让我困惑了一段时间,但我猜你没有两个 Webpack 入口点?一个用于客户端,一个用于服务器。

        如果我的假设是正确的,那么请查看沙盒存储库 here,其中我正在使用 Fluxible 为 Node server entrypointBrowser entrypoint 进行多次构建。

        不过,我现在还是不以为然,因为这只是一个供个人使用的测试项目。我还在本地 css 中使用了这种方法,它在服务器和客户端上都构建它,它就像一个魅力。

        编辑:我看到您使用的是 ES6,所以我假设您确实在构建服务器端?如果是这样,您不包含 CSS 的原因是什么?

        【讨论】:

        • 非常感谢您的好评。这是我第一次尝试同构应用程序,所以请原谅任何愚蠢的错误。我实际上是从 Fluxible 生成器开始的:github.com/yahoo/generator-fluxible 它不包含 CSS,所以我添加了加载器 (loader: 'style!css?modules&amp;localIdentName=[name]__[local]_[hash:base64:5]!postcss'),一切都运行良好,直到我尝试在服务器上使用 CSS。我收到此错误:SyntaxError: src/components/Application/Application.css: Unexpected token (2:0) &gt; 2 | @import 'styles/index.css';
        • 你的 CSS 文件是什么样的?你可以在你的问题中发布吗?而且你肯定在构建两个 Webpack 入口点。一个用于服务器,一个用于客户端?
        • 好的,更新了原始问题的更多细节。
        • 我不完全确定您为什么要将@imports 与本地 CSS 模块一起使用。您希望在使用它的 React 组件中而不是在 CSS 文件中要求该 CSS 文件。使用来自另一个 CSS 文件的类时,您应该使用本地 CSS 以 composes。这个 CSS 文件应该只包含需要它的组件所需的类,而不是引导其他 CSS 文件(我猜是吗?)。我无法解释为什么你只在服务器上遇到这个问题(但BROWSER: JSON.stringify(true) 显然总是返回 true 吗?)。换句话说,我不确定。
        • 看起来css-loader 确实被忽略了,所以@import 是无效的JS。
        【解决方案4】:

        所以你必须有两个 Webpack 配置,第一个用于编译浏览器包,第二个用于编译服务器包。

        使用标准配置你有css-loader?...:

        {
            test: /\.css$/,
            loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]_[hash:base64:5]'
        }
        

        你会得到两个不同的包。

        在服务器上。所有类都在css.locals对象中:

        let css = require('styles.scss');
        css.locals == { fooClass: 'some_foo_class' }
        

        在浏览器上没有.locals:

        let css = require('styles.scss');
        css == { fooClass: 'some_foo_class' }
        

        所以你需要在服务器版本中去掉.locals,这样它应该就像在浏览器中一样。

        您可以在 webpack 服务器配置中使用 css-loader/locals?... 来做到这一点:

        {
            test: /\.scss$/i,
            loader: "css/locals?modules&importLoaders=1&-minimize&localIdentName=[local]_[hash:3]!sass"
        }
        

        更多详情在这里https://github.com/webpack/css-loader/issues/59#issuecomment-109793167

        【讨论】:

          猜你喜欢
          • 2017-07-02
          • 2016-04-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-09
          • 1970-01-01
          相关资源
          最近更新 更多