【问题标题】:How to load styles onto React server-side using Webpack and go back to style-loader client-side?如何使用 Webpack 将样式加载到 React 服务器端并返回到样式加载器客户端?
【发布时间】:2016-02-14 20:13:50
【问题描述】:

我的目标是将 CSS 渲染到服务器端的样式标记中,或者与 style-loader 插件在客户端的操作方式完全相同。我知道这是不可能的,因为style-loader 直接写入 DOM,而 Node.js 中不存在 DOM。

目前我正在使用 ExtractTextPlugin,但是如果我没有将所有 CSS 编译到一个大文件中,那么当页面加载时它会丢失一些样式,除非我在服务器上运行 Webpack 而不是直接编译它.

我有这段代码渲染页面:

server.jsx

const renderedContent = renderToString(
    <Provider store={store} history={history}>
        <RoutingContext {...renderProps} />
    </Provider>
)

const finalState = store.getState()
const renderedPage = renderFullPage(renderedContent, finalState)

render-full-page.jsx

module.exports = function renderFullPage(renderedContent = undefined, state = {}) {
    return '<!DOCTYPE html>' + renderToStaticMarkup(
        <html lang="en">
        <head>
            <meta charSet="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            {typeof __production !== 'undefined' && __production === true && <link id="css" rel="stylesheet" href="/main.css" />}
        </head>
        <body>
            <div id="root"><div dangerouslySetInnerHTML={{__html: renderedContent}}></div></div>
            <script dangerouslySetInnerHTML={{__html: 'window.__INITIAL_STATE__ =' + JSON.stringify(state)}} />
            <script src="/bundle.js"></script>
        </body>
        </html>
    )
}

当我在我的 React 模块中需要样式时,我正在全局导入样式,如下所示:

import './../../assets/styl/flex-video'

我想更改加载 CSS 的方式,将所有 CSS 放入一个 var 中,我可以循环并输出 &lt;style&gt; 标签,就像 style-loader 这样做的方式一样:

{typeof __production !== 'undefined' && __production === true && cssFiles.map((styles) => {
    <style>{styles}</style>
})}

这在 Webpack 中可行吗?有没有像isomorphic-style-loader 这样的插件可以做到这一点?如果是这样,我将如何修改我的代码?

【问题讨论】:

    标签: reactjs webpack server-side react-jsx isomorphic-style-loader


    【解决方案1】:

    这是一个使用 isomorphic-style-loader 的示例。

    首先,您创建一个 Style Helper 来自动将样式推送到 DOM 或将它们加载到稍后将在服务器上呈现的 var 中。

    这是我创建的样式助手示例,经过修改以接受一组样式文件(CSS、Sass、LESS、Stylus):

    utilities/style-helper.jsx

    import React, { Component } from 'react'
    import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'
    
    const css = []
    
    const collectOrRender = function(styles) {
        let renderedCollection = []
    
        for (let i = 0, l = styles.length; i < l; i++) {
            const stylesFile = styles[i]
    
            if (canUseDOM) {
                renderedCollection[stylesFile._insertCss()]
            }
    
            css.push(stylesFile._getCss())
        }
    
        return renderedCollection
    }
    
    export function styleHelper(ComposedComponent, styles) {
        return class Styles extends Component {
            componentWillMount() {
                this.styleRemovers = collectOrRender(styles)
            }
    
            componentWillUnmount() {
                setTimeout(() => {
                    for (let i = 0, l = this.styleRemovers.length; i < l; i++) {
                        let styleRemover = this.styleRemovers[i]
                        typeof styleRemover === 'function' && styleRemover()
                    }
                }, 0)
            }
    
            render() {
                return <ComposedComponent {...this.props} />
            }
        }
    }
    
    export function renderStyles() {
        return css.join('')
    }
    

    然后你必须抓住你的 Style Helper 并让它在加载时包装你的模块。

    这个例子展示了在使用来自react-reduxconnect() 时如何做到这一点。

    my-module.jsx

    // Utilities
    import { styleHelper } from 'utilities/style-helper'
    
    const styles = [
        require('normalize.css'),
        require('styl/global'),
        require('styl/site'),
        require('styl/bubble')
    ]
    
    class myModule extends Component {
        render() { return (
            <div />
        )}
    }
    
    export default connect(
        state => ({ menuIsOpen: state.collapsibleMenu.menuIsOpen })
    )(styleHelper(myModule, styles))
    

    它处理客户端上的加载。现在在服务器上,您将所有这些样式存储到一个数组中。您需要将该数组拉入您的 HTML 页面并将其放置在一些 &lt;style&gt; 标记之间。

    这是我如何做到的一个例子:

    render-full-page.jsx

    import React from 'react'
    import { renderToStaticMarkup } from 'react-dom/server'
    
    // Utilities
    import { renderStyles } from './style-helper'
    
    module.exports = function renderFullPage(renderedContent = undefined, state = {}) {
        return '<!DOCTYPE html>' + renderToStaticMarkup(
            <html lang="en">
            <head>
    
                {/* Critical Styles */}
                <style dangerouslySetInnerHTML={{__html: renderStyles()}} />
            </head>
            <body>
                <div id="root" dangerouslySetInnerHTML={{__html: renderedContent}}></div>
                <script dangerouslySetInnerHTML={{__html: 'window.__INITIAL_STATE__ =' + JSON.stringify(state)}} />
                <script src="/bundle.js"></script>
            </body>
            </html>
        )
    }
    

    注意 style 标签如何从 Style Helper 获取样式并将它们放入 &lt;style&gt; 标签中。当 react 加载时,它会弄清楚要做什么,但现在服务器将只有实际正确显示页面所必需的 CSS。

    【讨论】:

      猜你喜欢
      • 2019-01-05
      • 2020-12-05
      • 2017-06-17
      • 2017-12-23
      • 2016-03-06
      • 2021-10-12
      • 2016-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多