Importing CSS files in Isomorphic React Components

2019-01-08 07:19发布

I have a React application with Components written in ES6 - transpiled via Babel and Webpack.

In some places I would like to include specific CSS files with specific Components, as suggested in react webpack cookbook

However, if in any Component file I require a static CSS asset, eg:

import '../assets/css/style.css';

Then the compilation fails with an error:

SyntaxError: <PROJECT>/assets/css/style.css: Unexpected character '#' (3:0)
    at Parser.pp.raise (<PROJECT>\node_modules\babel-core\lib\acorn\src\location.js:73:13)
    at Parser.pp.getTokenFromCode (<PROJECT>\node_modules\babel-core\lib\acorn\src\tokenize.js:423:8)
    at Parser.pp.readToken (<PROJECT>\node_modules\babel-core\lib\acorn\src\tokenize.js:106:15)
    at Parser.<anonymous> (<PROJECT>\node_modules\babel-core\node_modules\acorn-jsx\inject.js:650:22)
    at Parser.readToken (<PROJECT>\node_modules\babel-core\lib\acorn\plugins\flow.js:694:22)
    at Parser.pp.nextToken (<PROJECT>\node_modules\babel-core\lib\acorn\src\tokenize.js:98:71)
    at Object.parse (<PROJECT>\node_modules\babel-core\lib\acorn\src\index.js:105:5)
    at exports.default (<PROJECT>\node_modules\babel-core\lib\babel\helpers\parse.js:47:19)
    at File.parse (<PROJECT>\node_modules\babel-core\lib\babel\transformation\file\index.js:529:46)
    at File.addCode (<PROJECT>\node_modules\babel-core\lib\babel\transformation\file\index.js:611:24)

It seems that if I try and require a CSS file in a Component file, then the Babel loader will interpret that as another source and try to transpile the CSS into Javascript.

Is this expected? Is there a way to achieve this - allowing transpiled files to explicitly reference static assets that are not to be transpiled?

I have specified loaders for both .js/jsx and CSS assets as follows:

  module: {
    loaders: [
      { test: /\.css$/, loader: "style-loader!css-loader" },
      { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel'}
    ]
  }

View the full webpack config file

FULL DETAILS BELOW:

webpack.common.js - A base webpack config I use, so I can share properties between dev and production.

Gruntfile.js - Gruntfile used for development. As you can see it requires the webpack config above and adds some development properties to it. Could this be causing the problem?

Html.jsx - My HTML jsx component that tries to import/require the CSS. This is an isomorphic app (using Fluxbile), hence needing to have the actual HTML as a rendered component. Using the require statement seen in this file, in any part of my application, gives the error described.

It seems to be something to do with grunt. If I just compile with webpack --config webpack.common.js then I get no errors.

Short answer: It's a node runtime error. Trying to load CSS on the server in isomorphic apps is not a good idea.

9条回答
手持菜刀,她持情操
2楼-- · 2019-01-08 07:46

You can't require css in the component that you are rendering on the server. One way to deal with it is to check if it's a browser before requiring css.

if (process.env.BROWSER) {
  require("./style.css");
}

In order to make it possible you should set process.env.BROWSER to false (or delete it) on the server server.js

delete process.env.BROWSER;
...
// other server stuff

and set it to true for the browser. You do it with webpack's DefinePlugin in the config - webpack.config.js

plugins: [
    ...
    new webpack.DefinePlugin({
        "process.env": {
            BROWSER: JSON.stringify(true)
        }
    })
]

You can see this in action in gpbl's Isomorphic500 app.

查看更多
3楼-- · 2019-01-08 07:52

We had a similar problem with our isomorphic app (and a lot of other problems, you can find details here). As for the problem with CSS import, at first, we were using process.env.BROWSER. Later we've switched to babel-plugin-transform-require-ignore. It works perfectly with babel6.

All you need is to have the following section in your .babelrc

"env": {
   "node": {
     "plugins": [
       [
         "babel-plugin-transform-require-ignore", { "extensions": [".less", ".css"] }
       ]
     ]
   }
}

After that run your app with BABEL_ENV='node'. Like that:

BABEL_ENV='node' node app.js.

Here is an example of how a production config can look like.

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-01-08 07:55

Make sure you are using the loaders in your webpack config:

  module: {
     loaders: [
        { test: /\.jsx$/, exclude: /node_modules/, loader: "babel" },
        { test: /\.css$/, loader: "style!css" }
     ]
  }
查看更多
Melony?
5楼-- · 2019-01-08 07:57

If you're building an isomorphic app with ES6 and want to include CSS when rendering on the server (important so basic styles can be sent down to the client in the first HTTP response) check out the @withStyles ES7 decorator used in React Starter Kit.

This little beauty helps ensure users see your content with styles when the page is first rendered. Here's an example isomorphic app I'm building leveraging this technique. Just search the codebase for @withStyles to see how it's used. It goes a little something like this:

import React, { Component, PropTypes } from 'react';
import styles from './ScheduleList.css';
import withStyles from '../../decorators/withStyles';

@withStyles(styles)
class ScheduleList extends Component {
查看更多
聊天终结者
6楼-- · 2019-01-08 07:57

I've finally realised that this error is not originating at the compile stage, but rather at runtime. Because this is an ismorphic app, the components and any dependencies they have will first be parsed on the server (ie, in node). It is this that is causing the error.

Thanks for all the suggestions, I will post more if/when I figure out how to have per-component stylesheets in an isomorphic application.

查看更多
7楼-- · 2019-01-08 08:03

I also met the same problem when I want to do the server-side render.

So I write a postcss plugin, postcss-hash-classname.

You don't require css directly.

You require your css classname js file.

Because all you require is js file, you can do the server-side render as usual.

Besides, this plugin also use your classname and file path to generate unique hash to solve css scope problem.

You can try it!

查看更多
登录 后发表回答