SSR SCSS 的 Webpack 配置

IT技术 reactjs webpack sass server-side-rendering
2021-05-02 01:53:55

我有一个 React-TypeScript SSR 应用程序,我在其中使用 SCSS 文件进行样式设置。我需要在 Webpack 中编写规则来加载 SCSS,但我一直无法做到。

我在网上找到了各种解决方案,所有解决方案都非常复杂,并且使用了 mini-css-extract-plugin 之类的东西。我无法让他们中的任何一个工作。

我目前有两个 webpack 配置文件,一个用于客户端(web),一个用于服务器(节点),它们都加载 SCSS:

{
    test: /\.scss$/,
    use: ["css-loader", "sass-loader"]
}

我还遇到了另一个问题,我无法使用 style-loader,因为它会引发有关 window 对象的错误。有没有人有在 Webpack 中加载 SCSS 的工作示例(最好是简单的)?

2个回答

您可以使用 2 个网络配置文件走上正轨

https://gist.github.com/mburakerman/629783c16acf5e5f03de60528d3139af

但不要在 project.json 中设置任何其他配置文件,如 babel.rc .yaml 等或其他定义

试试这个

        test: /\.scss$/,
        use: [
            MiniCssExtractPlugin.loader,
            'css-loader',
            'postcss-loader',
            'sass-loader'
        ]

//..

plugins: [
    new MiniCssExtractPlugin({
        filename: 'assets/css/bundle-[contenthash].css',
        chunkFilename: 'assets/css/bundle-[contenthash].css'
    })
],

查看完整示例https://github.com/dewelloper/pzone/blob/master/webpack.config.store.js

使用服务器端呈现甲样板reactwebpackSass (用于CSS的module和纯SASS)

webpack.config.js

const path = require('path');
const isDevelopment = true;

module.exports = [
  {
    name: 'client',
    target: 'web',
    entry: './client.jsx',
    output: {
      path: path.join(__dirname, 'static'),
      filename: 'client.js',
      publicPath: '/static/',
    },
    resolve: {
      extensions: ['.js', '.jsx']
    },
    devtool: 'source-map',
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /(node_modules\/)/,
          use: [
            {
              loader: 'babel-loader',
            }
          ]
        },
        {
          test: /\.scss$/,
          use: [
            {
              loader: 'style-loader',
            },
            {
              loader: "css-loader",
              options: {
                modules: {
                  localIdentName: "[name]__[local]___[hash:base64:5]",
                },
                sourceMap: isDevelopment,
              }
            },
            {
              loader: 'sass-loader'
            }
          ]
        }
      ],
    },
  },
  {
    name: 'server',
    target: 'node',
    entry: './server.jsx',
    output: {
      path: path.join(__dirname, 'static'),
      filename: 'server.js',
      libraryTarget: 'commonjs2',
      publicPath: '/static/',
    },
    devtool: 'source-map',
    resolve: {
      extensions: ['.js', '.jsx']
    },
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /(node_modules\/)/,
          use: [
            {
              loader: 'babel-loader',
            }
          ]
        },
        {
          test: /\.scss$/,
          use: [
            {
              loader: 'isomorphic-style-loader',
            },
            {
              loader: "css-loader",
              options: {
                modules: {
                  localIdentName: "[name]__[local]___[hash:base64:5]",
                },
                sourceMap: isDevelopment,
              }
            },
            {
              loader: 'sass-loader'
            }
          ]
        }
      ],
    },
  }
];

开发依赖:

npm i -D @babel/cli @babel/preset-es2015  @babel/core @babel/plugin-proposal-class-properties @babel/preset-env @babel/preset-react babel-core babel-loader babel-plugin-lodash babel-plugin-react-transform babel-preset-env babel-preset-es2015 babel-preset-react babel-preset-stage-0 css-loader express isomorphic-style-loader node-sass sass-loader style-loader webpack webpack-dev-middleware webpack-hot-middleware webpack-hot-server-middleware

和依赖项:

npm i react react-dom react-helmet react-router-dom

服务器.jsx:

import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import {Helmet} from "react-helmet";
import Template from './template';
import App from './App';

export default function serverRenderer({ clientStats, serverStats }) {
    return (req, res, next) => {
        const context = {};
        const markup = ReactDOMServer.renderToString(
            <StaticRouter location={ req.url } context={ context }>
                <App />
            </StaticRouter>
        );
        const helmet = Helmet.renderStatic();

        res.status(200).send(Template({
            markup: markup,
            helmet: helmet,
        }));
    };
}

应用程序.jsx:

import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import Menu from './Menu'
import Helmet from "react-helmet";
import homepageStyles from './homepage.scss';

class Homepage extends Component {
    render() {
        return (
            <div className={ homepageStyles.component }>
                <Helmet
                    title="Welcome to our Homepage"
                />
                <Menu />
                <h1>Homepage</h1>
            </div>
        );
    }
}


class About extends Component {
    render() {
        return (
            <div>
                <h1>About</h1>
            </div>
        );
    }
}

class Contact extends Component {
    render() {
        return (
            <div>
                <h1>Contact</h1>
            </div>
        );
    }
}


export default class App extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <div>
                <Helmet
                    htmlAttributes={{lang: "en", amp: undefined}} // amp takes no value
                    titleTemplate="%s | React App"
                    titleAttributes={{itemprop: "name", lang: "en"}}
                    meta={[
                        {name: "description", content: "Server side rendering example"},
                        {name: "viewport", content: "width=device-width, initial-scale=1"},
                    ]}
                />
                <Switch>
                    <Route exact path='/' component={ Homepage } />
                    <Route path="/about" component={ About } />
                    <Route path="/contact" component={ Contact } />
                </Switch>
            </div>
        );
    }
}

模板.jsx

export default ({ markup, helmet }) => {
    return `<!doctype html>
<html ${helmet.htmlAttributes.toString()}>
<head>
    ${helmet.title.toString()}
    ${helmet.meta.toString()}
    ${helmet.link.toString()}
</head>
<body ${helmet.bodyAttributes.toString()}>
    <div id="root">${markup}</div>
    <div>Heeeeeeeeeeeeeeeeeeeelmet</div>
    <script src="/static/client.js" async></script>
</body>
</html>`;
};

菜单.jsx:

import { Link } from 'react-router-dom';
import React, { Component } from 'react';
import './menu.scss';

class Menu extends Component {
    render() {
        return (
            <div>
                <ul>
                    <li>
                        <Link to={'/'}>Homepage</Link>
                    </li>
                    <li>
                        <Link to={'/about'}>About</Link>
                    </li>
                    <li>
                        <Link to={'/contact'}>Contact</Link>
                    </li>
                </ul>
            </div>
        );
    }
}

export default Menu;

.babelrc:

{
 "presets": [
   "@babel/react",
   "@babel/preset-env"
 ],
 "plugins": [
   "@babel/proposal-class-properties"
 ]
}

主页.sccs

.component {
    color: blue;
}

菜单.scss:


li {
  background-color: yellow;
}

我使用了这篇文章:

https://blog.digitalkwarts.com/server-side-rendering-with-reactjs-react-router-v4-react-helmet-and-css-modules/