同构 React 应用程序中的 SCSS 编译
SCSS compilation in an isomorphic React app
我正在编写一个基于 :
的同构 React 应用程序
https://github.com/choonkending/react-webpack-node
我想使用 scss 而不是示例中使用的 css 模块。出于某种原因,我很难让他们工作。我的第一步是从服务器和客户端 configs 中删除 css
webpack 加载程序,用 scss
-specific 加载程序替换它们(以及删除 postcss
):
loaders: [
'style-loader',
'css-loader?modules&localIdentName=[name]_[local]_[hash:base64:3]',
'sass-loader?sourceMap',
]
但是当构建样式加载器显然不适合服务器端渲染时,这会抛出 ReferenceError: window is not defined
。所以我的下一个想法是使用 isomorphic-style-loader
。据我所知,要让它正常工作,我需要用它们的高阶组件 withStyles
:
来装饰我的组件
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from '../assets/scss/common/index.scss';
const App = (props, context) => (
<div className={classNames('app')}>
<h1 className="home_header">Welcome!</h1>
{props.children}
</div>
);
export default withStyles(s)(App);
然后在服务端的代码渲染页面做点小动作。但问题是,包文档中的示例显示了在 Express (https://libraries.io/npm/isomorphic-style-loader#webpack-configuration) 中触发的通量操作,而我正在使用的样板使用 react-router
。所以我有点迷路了,我应该如何将这个带有 insertCss
的对象注入到上下文中。我试过了:
import React from 'react';
import { renderToString } from 'react-dom/server';
import { RouterContext, match, createMemoryHistory } from 'react-router';
import axios from 'axios';
import { Provider } from 'react-redux';
import createRoutes from 'routes.jsx';
import configureStore from 'store/configureStore';
import headconfig from 'components/Meta';
import { fetchComponentDataBeforeRender } from 'api/fetchComponentDataBeforeRender';
const clientConfig = {
host: process.env.HOSTNAME || 'localhost',
port: process.env.PORT || '3001'
};
// configure baseURL for axios requests (for serverside API calls)
axios.defaults.baseURL = `http://${clientConfig.host}:${clientConfig.port}`;
function renderFullPage(renderedContent, initialState, head = {
title: 'cs3',
css: ''
}) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
${head.title}
${head.link}
<style type="text/css">${head.css.join('')}</style>
</head>
<body>
<div id="app">${renderedContent}</div>
<script type="text/javascript">window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};</script>
<script type="text/javascript" charset="utf-8" src="/assets/app.js"></script>
</body>
</html>
`;
}
export default function render(req, res) {
const history = createMemoryHistory();
const store = configureStore({
project: {}
}, history);
const routes = createRoutes(store);
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
const css = [];
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
const context = { insertCss: (styles) => css.push(styles._getCss()) };
const InitialView = (
<Provider context={context} store={store}>
<RouterContext {...renderProps} />
</Provider>
);
fetchComponentDataBeforeRender(store.dispatch, renderProps.components, renderProps.params)
.then(() => {
const componentHTML = renderToString(InitialView);
const initialState = store.getState();
res.status(200).end(renderFullPage(componentHTML, initialState, {
title: 'foo',
css
}));
})
.catch(() => {
res.end(renderFullPage('', {}));
});
} else {
res.status(404).send('Not Found');
}
});
}
但我仍然Warning: Failed context type: Required context 'insertCss' was not specified in 'WithStyles(App)'.
有什么解决办法吗?更重要的是——没有更简单的方法吗?这似乎需要做很多额外的工作。
当您进行服务器端渲染时,有几个部分可以处理 scss 与 webpack 的编译。首先,您不希望节点尝试将 .scss
文件导入您的组件。
所以在你的 webpack 配置中设置一个全局变量 WEBPACK: true
:
plugins: [
new webpack.DefinePlugin({
'process.env': {
WEBPACK: JSON.stringify(true),
}
})
],
并且在您的组件中,如果组件正在由 webpack 处理(在构建或开发期间),则仅尝试导入 .scss
文件:
if (process.env.WEBPACK) require('../assets/scss/common/index.scss');
如果每个组件只有一个 Sass 文件(您应该这样做),那么这始终只是一个单行文件。如果需要,可以在 index.scss
中导入任何其他 Sass 文件。
然后在您的配置中您可能仍然需要 css 加载程序,因此您的开发服务器应该如下所示:
{
test: /\.s?css$/,
loaders: ['style', 'css', 'sass']
},
还有类似这样的构建配置:
{
test: /\.s?css$/,
loader: ExtractTextPlugin.extract('style', 'css!sass')
},
我正在编写一个基于 :
的同构 React 应用程序https://github.com/choonkending/react-webpack-node
我想使用 scss 而不是示例中使用的 css 模块。出于某种原因,我很难让他们工作。我的第一步是从服务器和客户端 configs 中删除 css
webpack 加载程序,用 scss
-specific 加载程序替换它们(以及删除 postcss
):
loaders: [
'style-loader',
'css-loader?modules&localIdentName=[name]_[local]_[hash:base64:3]',
'sass-loader?sourceMap',
]
但是当构建样式加载器显然不适合服务器端渲染时,这会抛出 ReferenceError: window is not defined
。所以我的下一个想法是使用 isomorphic-style-loader
。据我所知,要让它正常工作,我需要用它们的高阶组件 withStyles
:
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from '../assets/scss/common/index.scss';
const App = (props, context) => (
<div className={classNames('app')}>
<h1 className="home_header">Welcome!</h1>
{props.children}
</div>
);
export default withStyles(s)(App);
然后在服务端的代码渲染页面做点小动作。但问题是,包文档中的示例显示了在 Express (https://libraries.io/npm/isomorphic-style-loader#webpack-configuration) 中触发的通量操作,而我正在使用的样板使用 react-router
。所以我有点迷路了,我应该如何将这个带有 insertCss
的对象注入到上下文中。我试过了:
import React from 'react';
import { renderToString } from 'react-dom/server';
import { RouterContext, match, createMemoryHistory } from 'react-router';
import axios from 'axios';
import { Provider } from 'react-redux';
import createRoutes from 'routes.jsx';
import configureStore from 'store/configureStore';
import headconfig from 'components/Meta';
import { fetchComponentDataBeforeRender } from 'api/fetchComponentDataBeforeRender';
const clientConfig = {
host: process.env.HOSTNAME || 'localhost',
port: process.env.PORT || '3001'
};
// configure baseURL for axios requests (for serverside API calls)
axios.defaults.baseURL = `http://${clientConfig.host}:${clientConfig.port}`;
function renderFullPage(renderedContent, initialState, head = {
title: 'cs3',
css: ''
}) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
${head.title}
${head.link}
<style type="text/css">${head.css.join('')}</style>
</head>
<body>
<div id="app">${renderedContent}</div>
<script type="text/javascript">window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};</script>
<script type="text/javascript" charset="utf-8" src="/assets/app.js"></script>
</body>
</html>
`;
}
export default function render(req, res) {
const history = createMemoryHistory();
const store = configureStore({
project: {}
}, history);
const routes = createRoutes(store);
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
const css = [];
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
const context = { insertCss: (styles) => css.push(styles._getCss()) };
const InitialView = (
<Provider context={context} store={store}>
<RouterContext {...renderProps} />
</Provider>
);
fetchComponentDataBeforeRender(store.dispatch, renderProps.components, renderProps.params)
.then(() => {
const componentHTML = renderToString(InitialView);
const initialState = store.getState();
res.status(200).end(renderFullPage(componentHTML, initialState, {
title: 'foo',
css
}));
})
.catch(() => {
res.end(renderFullPage('', {}));
});
} else {
res.status(404).send('Not Found');
}
});
}
但我仍然Warning: Failed context type: Required context 'insertCss' was not specified in 'WithStyles(App)'.
有什么解决办法吗?更重要的是——没有更简单的方法吗?这似乎需要做很多额外的工作。
当您进行服务器端渲染时,有几个部分可以处理 scss 与 webpack 的编译。首先,您不希望节点尝试将 .scss
文件导入您的组件。
所以在你的 webpack 配置中设置一个全局变量 WEBPACK: true
:
plugins: [
new webpack.DefinePlugin({
'process.env': {
WEBPACK: JSON.stringify(true),
}
})
],
并且在您的组件中,如果组件正在由 webpack 处理(在构建或开发期间),则仅尝试导入 .scss
文件:
if (process.env.WEBPACK) require('../assets/scss/common/index.scss');
如果每个组件只有一个 Sass 文件(您应该这样做),那么这始终只是一个单行文件。如果需要,可以在 index.scss
中导入任何其他 Sass 文件。
然后在您的配置中您可能仍然需要 css 加载程序,因此您的开发服务器应该如下所示:
{
test: /\.s?css$/,
loaders: ['style', 'css', 'sass']
},
还有类似这样的构建配置:
{
test: /\.s?css$/,
loader: ExtractTextPlugin.extract('style', 'css!sass')
},