同构 React 应用程序中的 SCSS 编译 [英] SCSS compilation in an isomorphic React app

查看:51
本文介绍了同构 React 应用程序中的 SCSS 编译的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个基于以下内容的同构 React 应用程序:

https://github.com/choonkending/react-webpack-node

我想使用 scss 而不是示例中使用的 css 模块.出于某种原因,我很难让他们工作.我的第一步是从服务器和客户端删除 css webpack loader configs 将它们替换为 scss 特定的加载程序(以及删除 postcss):

 加载器:['样式加载器','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';从类名"导入类名;从 'isomorphic-style-loader/lib/withStyles' 导入 withStyles;从'../assets/scss/common/index.scss'导入s;const App = (props, context) =>(<div className={classNames('app')}><h1 className="home_header">欢迎!</h1>{props.children}

);导出默认 withStyles(s)(App);

然后在服务器上的代码渲染页面中做一些诡计.但问题是,包文档中的示例显示在 Express (https://libraries.io/npm/isomorphic-style-loader#webpack-configuration),我使用的样板使用 react-router.所以我有点迷茫,我应该如何将这个带有 insertCss 的对象注入到上下文中.我试过这个:

从'react'导入React;import { renderToString } from 'react-dom/server';import { RouterContext, match, createMemoryHistory } from 'react-router';从 'axios' 导入 axios;从'react-redux'导入{提供者};从'routes.jsx'导入createRoutes;从商店/配置商店"导入配置商店;从组件/元"导入 headconfig;从 'api/fetchComponentDataBeforeRender' 导入 { fetchComponentDataBeforeRender };const clientConfig = {主机:process.env.HOSTNAME ||'本地主机',端口:process.env.PORT ||'3001'};//为 axios 请求配置 baseURL(用于服务器端 API 调用)axios.defaults.baseURL = `http://${clientConfig.host}:${clientConfig.port}`;函数 renderFullPage(renderedContent, initialState, head = {标题:'cs3',css: ''}) {返回`<!DOCTYPE html><html lang="zh-cn"><头>${head.title}${head.link}<style type="text/css">${head.css.join('')}</style><身体><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></html>`;}导出默认函数渲染(req,res){const 历史 = createMemoryHistory();const store = configureStore({项目: {}}, 历史);const 路由 = createRoutes(store);match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {const css = [];如果(错误){res.status(500).send(error.message);} else if(重定向位置){res.redirect(302, redirectLocation.pathname + redirectLocation.search);} else if (renderProps) {const context = { insertCss: (styles) =>css.push(styles._getCss()) };const 初始视图 = (<Provider context={context} store={store}><RouterContext {...renderProps}/></提供者>);fetchComponentDataBeforeRender(store.dispatch, renderProps.components, renderProps.params).then(() => {const componentHTML = renderToString(InitialView);const initialState = store.getState();res.status(200).end(renderFullPage(componentHTML, initialState, {标题:'富',css}));}).catch(() => {res.end(renderFullPage('', {}));});} 别的 {res.status(404).send('未找到');}});}

但我仍然收到 警告:失败的上下文类型:WithStyles(App)"中未指定所需的上下文insertCss". 任何想法如何解决这个问题?更重要的是 - 没有更简单的方法吗?这似乎需要很多额外的工作.

解决方案

当您进行服务器端渲染时,有几个部分可以使用 webpack 处理 scss 编译.首先,您不希望 node 尝试将 .scss 文件导入到您的组件中.

所以在你的 webpack 配置中设置一个全局变量 WEBPACK: true:

插件:[新的 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 加载器,因此对于您的开发服务器,它应该如下所示:

<代码>{测试:/\.s?css$/,加载器:['style', 'css', 'sass']},

为您构建配置的类似内容:

<代码>{测试:/\.s?css$/,loader: ExtractTextPlugin.extract('style', 'css!sass')},

I'm writing an isomorphic React app based on :

https://github.com/choonkending/react-webpack-node

Instead of css modules used in the examples I'd like to use scss though. And for some reason I'm having a really hard time getting them to work. My first step was to remove the css webpack loaders from both the server and the client configs replacing them with scss-specific loaders (as well as removing postcss) :

  loaders: [
    'style-loader',
    'css-loader?modules&localIdentName=[name]_[local]_[hash:base64:3]',
    'sass-loader?sourceMap',
  ]

But this throws ReferenceError: window is not defined when building as style-loader is apparently not suitable for server-side rendering. So my next idea was to use isomorphic-style-loader. As far as I understand to get it working I need to decorate my component with their higher order component 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);

and then do some trickery in the code rendering page on the server. But the problem is, example from the package docs shows a flux action fired inside Express (https://libraries.io/npm/isomorphic-style-loader#webpack-configuration), and the boilerplate that I'm using uses react-router. So I'm kinda lost as how should I inject this object with insertCss into context. I tried this :

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');
    }
  });
}

but I'm still getting Warning: Failed context type: Required context 'insertCss' was not specified in 'WithStyles(App)'. Any ideas how to tackle this ? And more importantly - is there no easier way to do it ? This seems like a lot of additional work.

解决方案

There's a few parts to handling scss compilation with webpack when you're doing server-side rendering. First of all, you don't want node trying to import .scss files into your components.

So set a global variable WEBPACK: true in your webpack config:

plugins: [
    new webpack.DefinePlugin({
        'process.env': {
            WEBPACK: JSON.stringify(true),
        }
    })
],

And in your components, only attempt to import .scss files if the component is being handled by webpack (either during build or development):

if (process.env.WEBPACK) require('../assets/scss/common/index.scss');

If you only have one Sass file per component (you should) then this is always just a one-liner. Any additional Sass files can be imported inside index.scss if you need to.

Then in your config you probably still want the css loader, so it should look like this for your dev server:

{
    test: /\.s?css$/,
    loaders: ['style', 'css', 'sass']

},

And something like this for you build config:

{
    test: /\.s?css$/,
    loader: ExtractTextPlugin.extract('style', 'css!sass')
},

这篇关于同构 React 应用程序中的 SCSS 编译的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆