React + Material UI +打字稿+ Webpack + SSR不起作用

时间:2019-02-14 20:27:55

标签: javascript reactjs typescript material-ui serverside-rendering

我有这个要在服务器端渲染的应用程序。一切正常,直到我尝试向其添加Material UI。

我的目录结构是这样:

app/
   build/ * This is created by webpack
        server_bundle.js
        public/
              client_bundle.js
              fonts/
              images/
   src/
      client/
            Client.tsx
      server/
            server.tsx
      shared/
            App.tsx
            routes.tsx
   webpack.config.js

这是我的文件内容:

webpack.config.js

const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    target: 'node',
    entry: {
        server: path.resolve(__dirname, 'src/server/server.tsx'),
        "public/client": path.resolve(__dirname, 'src/client/client.tsx')

    },
    output: {
        filename: '[name]_bundle.js',
        path: path.resolve(__dirname, 'build'),
        publicPath: '/build'
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx']
    },
    module: {
        rules: [{
                test: /\.(tsx|ts)?$/,
                loader: 'awesome-typescript-loader',
                options: {
                    jsx: 'react'
                }
            },
            {
                test: /\.(scss|sass|css)$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: true
                        }
                    },
                ]
            },
            {
                test: /\.(ico)$/,
                loader: 'file-loader',
                options: { outputPath: '/public', publicPath: '/public', name: '[name].[ext]' }
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif)$/,
                loader: 'file-loader',
                options: { outputPath: '/public/images', publicPath: 'images' }
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                loader: 'file-loader',
                options: { outputPath: '/public/fonts', publicPath: 'fonts' }
            },
        ]
    },
    optimization: {
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'public/styles_bundle.css',
            chunkFilename: "public/styles/[id].css"
        })
    ]
}

server.tsx

import * as express from "express";
import * as bodyParser from "body-parser";
import * as React from "react";
import * as ReactDOMServer from "react-dom/server";
import {StaticRouter} from "react-router";
import { matchPath } from "react-router-dom";
import {Helmet} from "react-helmet";
import App from "../shared/App";
import routes from '../shared/routes';

const app = express();
const PORT = process.env.PORT || 3000;

app.use(bodyParser.urlencoded());
app.use(bodyParser.json());
app.use(express.static("build/public"));

app.get('*', (req, res, next) => {

    const activeRoute = routes.find(route => !!matchPath(req.url, route)) || {path: "/"};

    const now = new Date();
    console.log(`GET ${now} - ${req.url}`);

    const context = {}
    const content = ReactDOMServer.renderToString(
            <StaticRouter location={req.url} context={context}>
                <App />
            </StaticRouter>
    );

    const helmet = Helmet.renderStatic();

    const html = `
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            ${helmet.title.toString()}
            ${helmet.meta.toString()}
            <link rel="stylesheet" href="styles_bundle.css">
            <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
            <link rel="icon" href="/favicon.ico" type="image/x-icon" />
        </head>
        <body>
            <div id="root" style="overflow-x: hidden; width: 100%; margin: 0;">${content}</div>
            <script src="client_bundle.js"></script>
        </body>
        </html>
    `;

    res.send(html);
});



app.listen(PORT, () => {
    console.log(`App is running on port ${PORT}`)
})

Client.tsx

import * as React from "react";
import * as ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";

import App from "../shared/App";

// Because it's already been rendered, we only need to hydrate event
// handlers and wire things up. 
ReactDOM.hydrate(
    <BrowserRouter>
        <App />
    </BrowserRouter>,
    document.querySelector("#root")
);

App.tsx

import * as React from 'react';
import routes from "../shared/routes";
import { Helmet } from 'react-helmet';
import { Switch, Route } from "react-router";
import 'typeface-roboto';

class App extends React.Component {
    render() {
        return (
            <React.Fragment>            

                <Switch>
                    {routes.map(({path, exact, component: C}) => {
                        return <Route 
                            path={path} 
                            exact={exact}                                                       
                            render={(props) => <C {...props}/> }
                        />
                    })}                   
                </Switch>

            </React.Fragment>
        )
    }
}

export default App;

最后, routes.tsx

import * as React from 'react';
import { Button } from '@material-ui/core';

const routes = [
    {
        name: "Home",
        exact: true,
        path: "/",
        component: (props:any) => {return (

            <Button variant="contained" color="primary">
                Hello World
            </Button>
        )}
    }
];

export default routes;

我在浏览器控制台中遇到此错误,并且显然没有任何实质性的ui样式:

client_bundle.js:44 Uncaught ReferenceError: global is not defined
    at Object.<anonymous> (client_bundle.js:44)
    at n (client_bundle.js:1)
    at Object.<anonymous> (client_bundle.js:1)
    at n (client_bundle.js:1)
    at Object.<anonymous> (client_bundle.js:17)
    at n (client_bundle.js:1)
    at Object.<anonymous> (client_bundle.js:17)
    at n (client_bundle.js:1)
    at Object.<anonymous> (client_bundle.js:36)
    at n (client_bundle.js:1)

client_bundle.js的那部分看起来像这样:

... __esModule",{value:!0});global.CSS;t.default=function(e){return e}} ...

您认为这里可能会发生什么?

1 个答案:

答案 0 :(得分:1)

 try this workaround in your 

 plugins: [
            new webpack.DefinePlugin({
           'global': {} //  webpack workaround
          }),
            new MiniCssExtractPlugin({
            filename: 'public/styles_bundle.css',
            chunkFilename: "public/styles/[id].css"
            })
          ]