如何使用 Webpack 将样式加载到 React 服务器端并返回到样式加载器客户端?
How to load styles onto React server-side using Webpack and go back to style-loader client-side?
我的目标是将 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 放入一个 var 我可以循环并以相同的方式输出 <style>
标签 style-loader
是这样吗:
{typeof __production !== 'undefined' && __production === true && cssFiles.map((styles) => {
<style>{styles}</style>
})}
这在 Webpack 中可行吗?是否有像 isomorphic-style-loader
这样的插件可以做到这一点?如果是这样,我将如何修改我的代码?
这是您想要使用 isomorphic-style-loader
的示例。
首先,您创建一个样式助手来自动将样式推送到 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-redux
中的 connect()
时执行此操作。
我的-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来正确显示页面。
我的目标是将 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 放入一个 var 我可以循环并以相同的方式输出 <style>
标签 style-loader
是这样吗:
{typeof __production !== 'undefined' && __production === true && cssFiles.map((styles) => {
<style>{styles}</style>
})}
这在 Webpack 中可行吗?是否有像 isomorphic-style-loader
这样的插件可以做到这一点?如果是这样,我将如何修改我的代码?
这是您想要使用 isomorphic-style-loader
的示例。
首先,您创建一个样式助手来自动将样式推送到 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-redux
中的 connect()
时执行此操作。
我的-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来正确显示页面。