I am rendering pages server-side with React router's StaticRouter
.
express.js (server)
const html = ReactDOMServer.renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); res.status(200).send(` <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="./dist/client/app.css" type="text/css"/> </head> <body> <div id="app">${html}</div> </body> </html> `);
Serving of static files:
app.use(express.static(path.resolve(__dirname, "../dist/client")));
App.js (shared)
import React from "react"; import { Switch, Route } from "react-router"; export default () => { return ( <Switch> ... </Switch> ); };
index.jsx (client)
import React from "react"; import { BrowserRouter } from "react-router-dom"; import ReactDOM from "react-dom"; import App from "./App"; ReactDOM.render( <BrowserRouter> <App/> </BrowserRouter>, document.getElementById("app") );
./styles/Main.scss
.header { background-color: #002933; }
I have 2 webpack configurations, 1 for the client & 1 for the server:
webpack.config.dev.js
const nodeExternals = require("webpack-node-externals"); const webpack = require("webpack"); const ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { devtool: "cheap-module-eval-source-map", entry: { app: [ "eventsource-polyfill", "webpack-hot-middleware/client", "webpack/hot/only-dev-server", "react-hot-loader/patch", "./client/index.jsx", ], vendor: [ "react", "react-dom", ], }, output: { path: `${__dirname}/dist/client`, filename: "app.min.js", publicPath: "http://0.0.0.0:8000/" }, resolve: { extensions: [ ".js", ".jsx" ], modules: [ "node_modules" ], }, externals: [ nodeExternals() ], module: { loaders: [ ... }, { test: /\.scss$/, exclude: /node_modules/, loader: ExtractTextPlugin.extract({ fallback: "style-loader", use: [ { loader: "css-loader", query: { localIdentName: "[hash:8]", modules: true } }, { loader: "postcss-loader" }, { loader: "sass-loader" } ] }), }, ], }, plugins: [ new ExtractTextPlugin({ filename: "[name].css", allChunks: true }), ] };
webpack.config.server.js
const ExternalsPlugin = require("webpack-externals-plugin"); module.exports = { entry: `${__dirname}/server/server.js`, output: { path: `${__dirname}/dist/`, filename: "server.bundle.js", }, target: "node", node: { __filename: true, __dirname: true, }, resolve: { extensions: [ ".js", ".jsx", ], modules: [ "client", "node_modules", ], }, module: { loaders: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: "babel-loader", }, { test: /\.scss$/, loader: 'style-loader!css-loader/locals?module&localIdentName=[name]__[local]___[hash:base64:5]!sass-loader', }, ], }, plugins: [ new ExternalsPlugin({ type: "commonjs", include: `${__dirname}/node_modules/`, }), ], };
I have a JSX file where the .header
should be applied to:
import React from "react"; import Links from "./Links.jsx"; import profilePic from "../../img/brand/profilePic.jpg"; import styles from "../../styles/Main.scss"; export default class Header extends React.Component { constructor() { super(); } render() { return ( <header className={styles.header}> <img src={profilePic} alt="Professional Picture"/> <h5>{this.props.pageName}</h5> <Links/> </header> ); } }
This throws the error:
TypeError: Cannot read property 'header' of undefined at Header.render (E:/Documents/Projects/website/client/js/components/Header.jsx:22:30) at resolve (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2149:18) at ReactDOMServerRenderer.render (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2260:22) at ReactDOMServerRenderer.read (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2234:19) at Object.renderToString (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2501:25) at E:/Documents/Projects/website/server/config/lib/express.js:204:31 at Layer.handle [as handle_request] (E:\Documents\Projects\website\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:317:13) at E:\Documents\Projects\website\node_modules\express\lib\router\index.js:284:7 at Function.process_params (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:335:12) at next (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:275:10) at p3p (E:\Documents\Projects\website\node_modules\lusca\lib\p3p.js:15:9) at E:\Documents\Projects\website\node_modules\lusca\index.js:59:28 at xframe (E:\Documents\Projects\website\node_modules\lusca\lib\xframes.js:12:9) at E:\Documents\Projects\website\node_modules\lusca\index.js:59:28 at xssProtection (E:\Documents\Projects\website\node_modules\lusca\lib\xssprotection.js:16:9)
When running the application, webpack reports that the stylesheet has been loaded:
EDIT
Other than an ES6 import, I have attempted to use CommonJS' require()
as in MERN but still no look...
When I build my server webpack config, I am now getting the error:
ERROR in (webpack)-dev-middleware/node_modules/mime/index.js Module not found: Error: Can't resolve './types/standard' in 'E:\Documents\Projects\website\node_modules\webpack-dev middleware\node_modules\mime' @ (webpack)-dev-middleware/node_modules/mime/index.js 4:26-53 @ (webpack)-dev-middleware/index.js @ ./server/config/lib/express.js @ ./server/config/lib/app.js @ ./server/server.js
I am not sure if this a red-herring or not in this situation or not. I am also getting an error thrown from Babel whenever I try to start my project:
SyntaxError: E:/Documents/Projects/website/client/styles/Main.scss: Unexpected token (1:0) > 1 | .header { | ^ 2 | background-color: #002933; 3 | } 4 | at Parser.pp$5.raise (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:4454:13) at Parser.pp.unexpected (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:1761:8) at Parser.pp$3.parseExprAtom (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3750:12) at Parser.parseExprAtom (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:7238:22) at Parser.pp$3.parseExprSubscripts (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3494:19) at Parser.pp$3.parseMaybeUnary (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3474:19) at Parser.pp$3.parseExprOps (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3404:19) at Parser.pp$3.parseMaybeConditional (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3381:19) at Parser.pp$3.parseMaybeAssign (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3344:19) at Parser.parseMaybeAssign (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:6474:20) at Parser.pp$3.parseExpression (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3306:19) at Parser.pp$1.parseStatement (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:1906:19) at Parser.parseStatement (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:5910:22) at Parser.pp$1.parseBlockBody (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:2268:21) at Parser.pp$1.parseTopLevel (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:1778:8) at Parser.parse (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:1673:17)
Whilst playing around with this issue, I have not yet encountered that error. This is my .babelrc
:
{ "presets": [ "react", "es2015", "stage-0" ], "plugins": [ "react-hot-loader/babel", "transform-decorators-legacy" ], "env": { "server": { "plugins": [ [ "css-modules-transform", { "preprocessCss": "./loaders/sass-loader.js", "generateScopedName": "[hash:8]", "extensions": [".scss"] } ] ] }, "production": { "presets": [ "es2015", "react", "react-optimize", "es2015-native-modules", "stage-0" ] } } }
2 Answers
Answers 1
I think you have an error in your webpack.config json hierarchy for you css/sass loaders. Replace your "loaders" array under module with this "rules" array:
module: { rules: [ { test: /\.scss$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: ['css-loader', 'sass-loader'] }), }, { test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader" }) }, ], },
You can see more examples of ExtractTextPlugin usage here: https://github.com/webpack-contrib/extract-text-webpack-plugin#usage
Answers 2
You're having issues because you're using css-loader/locals
but not using ExtractTextPlugin
(at least in Development). https://github.com/webpack-contrib/css-loader/issues/59
Note: For prerendering with extract-text-webpack-plugin you should use css-loader/locals instead of style-loader!css-loader in the prerendering bundle. It doesn't embed CSS but only exports the identifier mappings.
This also explains why you're not able to access the style variable .theHeader
.
Also, the error Resource interpreted as Stylesheet but transferred with MIME type text/html: "http://localhost:3000/app.css".
is a red herring. That is simply the error message you receive if you try to load a stylesheet that doesn't even exist. Which in this case, doesn't appear to be in the directory that you think it is, or isn't actually being generated into a file there.
Since ExtractTextPlugin
is disabled in development, its likely that your CSS is only being processed by css-loader/locals
. This might not be a problem in production, since it pairs with ExtractTextPlugin
but could explain your problems of running this in development.
Update:
In looking into this over a little more, I came across a blog post that I think might help you figure out how to configure your CSS to work how you want. https://medium.com/@mattvagni/server-side-rendering-with-css-modules-6b02f1238eb1
I think the main reason for the complication here is that you're sending over the HTML as a rendered string via ReactDOMServer. So there is no where for webpack to inject a <link>
tag into. You might want to consider just having a <link>
tag in your header to reference your desired CSS file, since webpack is going to create a single CSS file anyways.
Finally, I highly recommend studying a bit more on webpack, especially since SSR is a newer process and requires doing things a bit differently than many have initially anticipated when webpack was first created.
0 comments:
Post a Comment