3

I'm currently wiring up an application with SSR in react. However, I'm having issues with the server NOT returning responses in a Brotli compressed file format. I do see that my br files are getting generated by webpack in my client webpack config file. But my server does not pick up on returning br files back to my client. FYI....I also have my webpack for the client generate gz files as well. The files I'm trying to return back in br mode is the client_bundle.js file and app.css.

As you will see in my server.js file I send back a response in an HTML string format. I have tried changing my link and script tags to return app.css.br and client_bundle.js.br but no avail.

Do you know what I may be doing wrong and why my server won't return and/or client will not pickup the br files for CSS and JS?

FYI....I'm using nodejs, express-static-zip in my server.js file. In addition, I'm using compression-webpack-plugin and brotli-webpack-plugin to generate the br and gz files for app.css and client_bundle.js. Another side note, I'm building my client files into build/public and my server files into build.

Thanks for any assistance.

webpack.client.js

const path = require("path");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const webpack = require("webpack");
const autoprefixer = require("autoprefixer");
const CompressionPlugin = require("compression-webpack-plugin");
const BrotliPlugin = require("brotli-webpack-plugin");

module.exports = {
    mode:"development",
  optimization: {
    usedExports: true
  },
  entry: "./src/client/client.js",
  output: {
    filename: "client_bundle.js",
    path: path.resolve(__dirname, "build/public/"),
    publicPath: "/build/public/"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: "/node_modules"
      },
      {
        test: /\.scss$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: ["css-loader", "postcss-loader", "sass-loader"]
        })
      },
      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        use: [
          {
            loader: "file-loader",
            options: {
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
        filename: "app.css"
    }),
      new CompressionPlugin({
          filename: '[path].gz[query]'
      }),
      new BrotliPlugin({
          asset: '[path].br[query]',
          test: /\.(js|css|html|svg)$/,
          threshold: 10240,
          minRatio: 0.8
      })
  ]
};

Server.js

import express from "express";
import cors from "cors";
import React from "react";
import { renderToString } from "react-dom/server";
import { Provider } from "react-redux";
import { StaticRouter, matchPath } from "react-router-dom";
import serialize from "serialize-javascript";
import routes from "../shared/routes";
import configureStore from "../shared/configureStore";
import App from "../shared/app";
import "source-map-support/register";
import http from "http";
import reload from "reload";
import expressStaticGzip from "express-static-gzip";

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

app.use(cors());
app.use("/build/public", expressStaticGzip('/build/public', {
    enableBrotli: true,
    orderPreference: ['br', 'gz'],
    setHeaders: function (res, path) {
        res.setHeader("Cache-Control", "public, max-age=31536000");
    }
}));
app.use(express.static("build/public"));

var server = http.createServer(app);
reload(app);
      acc.push(Promise.resolve(store.dispatch(route.component.initialData())));
    }
    return acc;
  }, []);
   const currentRoute = routes.find(route => matchPath(req.url, route));
   const requestInitialData =
   currentRoute.component.initialData && currentRoute.component.initialData();
  Promise.all(promises)
    .then(() => {
      const context = {};
      const markup = renderToString(
        <Provider store={store}>
          <StaticRouter location={req.url} context={context}>
            <App />
          </StaticRouter>
        </Provider>
      );

      const initialData = store.getState();
      res.send(`
        <!DOCTYPE html>
        <html>
          <head>
            <basehref="/">
            <title>Sample</title>
            <link rel="stylesheet" href="app.css.br">
          </head>

          <body>
            <div id="root">${markup}</div>
             <script src="client_bundle.js.br"></script>
             <script src="/reload/reload.js"></script>
            <script>window.__initialData__ = ${serialize(initialData)}</script>
          </body>
        </html>
      `);
    })
    .catch(next);
});

app.listen(PORT, () => {
  console.log("App running,", PORT);
});

4

1 回答 1

0

您正在压缩 Br 和 Gzip 中的客户端文件,这看起来是正确的,因为您的应用程序从build/public.

但是服务器端渲染,它的响应是一个 HTML 页面,看起来并没有使用 Brotli 或 Gzip 进行压缩。因此,浏览器会访问您的服务器,它会以未压缩的 HTML 响应进行响应,然后在 React 进行水合之前下载 Brotli 压缩文件。

于 2019-02-21T16:47:56.867 回答