如何使用Webpack将样式加载到React服务器端并返回到样式加载器客户端?

时间:2016-02-14 20:13:51

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

我的目标是将CSS渲染到样式标记服务器端,类似于style-loader插件如何在客户端执行。我知道这是不可能的,因为style-loader直接写入DOM并且DOM在Node.js中不存在。

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

我有这个代码呈现页面:

server.jsx

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

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

渲染全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中,我可以循环输出<style>标记,style-loader就像{typeof __production !== 'undefined' && __production === true && cssFiles.map((styles) => { <style>{styles}</style> })} 一样这样:

isomorphic-style-loader

这可以在Webpack中使用吗?是否有{% block content %} <body> <div class="container center_div" align=middle> <h2>Indie/Alternative Artist Recommendation</h2> <p>Enter 5 artists below and we will provide similar artists you may like</p> <br/> <form method="Post" action="/results"> <div class="col-xs-4" align=middle> {{ form.artist1.label(class="col-md-6 col-sm-12") }} {{ form.artist1(class="form-control") }} {{ form.artist2.label(class="col-lg-6 col-md-6 col-sm-12") }} {{ form.artist2(class="form-control") }} {{ form.artist3.label(class="col-lg-6 col-md-6 col-sm-12") }} {{ form.artist3(class="form-control") }} {{ form.artist4.label(class="col-lg-6 col-md-6 col-sm-12") }} {{ form.artist4(class="form-control") }} {{ form.artist5.label(class="col-lg-6 col-md-6 col-sm-12") }} {{ form.artist5(class="form-control") }} <p><input type=submit value=Go type="button" class="btn btn-default"></p> </div> </form> </div> {% block scripts %} <script src="{{url_for('.static', filename='js/getArtists.js')}}"></script> {{super()}} {% endblock %} 这样的插件能够做到这一点?如果是这样,我将如何修改我的代码?

1 个答案:

答案 0 :(得分:1)

以下是您使用isomorphic-style-loader所需内容的示例。

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

这是我创建的样式助手的一个例子,它被修改为接受样式文件数组(CSS,Sass,LESS,Stylus):

<强>公用事业/样式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并让你的模块加载。

此示例显示了在connect()使用react-redux时如何执行此操作。

我-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页面并将其放在某些<style>标记之间。

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

<强>渲染全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 Helper获取样式并将它们放在<style>标记内。当反应加载时,它将确定要做什么,但现在服务器将只有必要的CSS才能正确显示页面。