Как загрузить стили на серверную часть React с помощью Webpack и вернуться к клиентской стороне загрузчика стилей?

Моя цель состоит в том, чтобы сделать CSS похожим на тег сервера на стороне сервера или как style-loader Плагин будет делать это на стороне клиента. Я знаю, что это невозможно, потому что style-loader пишет напрямую в DOM, а DOM не существует в Node.js.

В настоящее время я использую 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)

рендеринга полный 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 в переменную, которую я могу просмотреть и вывести <style> теги так же style-loader это так:

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

Это возможно в Webpack? Есть ли плагин, как isomorphic-style-loader что способен на это? Если так, как бы я изменил свой код?

1 ответ

Решение

Вот пример того, что вы хотите использовать isomorphic-style-loader,

Сначала вы создаете помощник по стилю, чтобы автоматизировать передачу стилей в DOM или загрузку их в переменную, которая впоследствии будет отображаться на сервере.

Это пример помощника по стилю, который я создал, модифицированный для приема массива файлов стилей (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('')
}

Затем вы должны взять свой помощник по стилю и заставить его обернуть ваш модуль под нагрузкой.

Этот пример показывает, как вы это сделаете, используя 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 получает стили от Style Helper и помещает их в <style> тег. Когда реагирует загрузка, он выяснит, что делать, но теперь на сервере будет только CSS, необходимый для правильного отображения страницы.

Другие вопросы по тегам