Serving gzip files in React server

2019-06-02 00:40发布

问题:

I'm having trouble actually serving a gzip compression of my bundle.js file in React. I am trying to reduce the size of this dramatically, and I have done uglify and dedupe, etc ... and it's gone down from 2.9mb to 2.6mb which is really weird to me. I am now using the compression plugin and I get an outputted gzip file, but now I'm still serving the bundle.js and not the bundle.js.gz.

I don't want to use the compression middleware from express, because I'm doing the gzip in the build process. Anyways, here is my distServer file:

import express from 'express';
import path from 'path';
import open from 'open';

/* eslint-disable no-console */

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


app.use(express.static('dist'));

app.get('*.js', function(req, res, next) {
 req.url = req.url + '.gz';
 res.set('Content-Encoding', 'gzip');
 res.set('Content-Type', 'text/javascript');
 next();
});

app.get('*.css', function(req, res, next) {
 req.url = req.url + '.gz';
 res.set('Content-Encoding', 'gzip');
 res.set('Content-Type', 'text/css');
 next();
});

app.get('*', function(req, res) {
  res.sendFile(path.join( __dirname, '../dist/index.html'));
});

app.listen(port, function(err) {
  if (err) {
    console.log(err);
  } else {
    open(`http://localhost:${port}`);
  }
});

And my webpack config is:

import webpack from 'webpack';
import path from 'path';
import ExtractTextPlugin from 'extract-text-webpack-plugin';

var CopyWebpackPlugin = require('copy-webpack-plugin');
var CompressionPlugin = require("compression-webpack-plugin");
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

export default {
  devtool: 'source-map',
  noInfo: false,
  entry: [
    './src/index'
  ],
  target: 'web',
  output: {
    path: __dirname + '/dist', // Note: Physical files are only output by the production build task `npm run build`.
    publicPath: '/',
    filename: 'bundle.js'
  },
  plugins: [
    // new BundleAnalyzerPlugin(),
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.DefinePlugin({
      'process.env': {NODE_ENV: '"production"'}
    }),
    new ExtractTextPlugin('styles.css'),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({
      mangle: true,
      compress: {
        warnings: false, // Suppress uglification warnings
        pure_getters: true,
        unsafe: true,
        unsafe_comps: true,
        screw_ie8: true
      },
      output: {
        comments: false,
      },
      exclude: [/\.min\.js$/gi] // skip pre-minified libs
    }),
    new CopyWebpackPlugin([
      { from: 'src/robots.txt', to: 'robots.txt' },
      { from: 'src/sitemap.xml', to: 'sitemap.xml' }
    ], {
      copyUnmodified: true
    }),
    new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/]),
    new CompressionPlugin({
      asset: "[path].gz[query]",
      algorithm: "gzip",
      test: /\.js$|\.css$|\.html$/,
      threshold: 10240,
      minRatio: 0
    })
  ],
  module: {
    loaders: [
      {test: /\.js$/, include: path.join(__dirname, 'src'), loaders: ['babel']},
      {test: /(\.css)$/, loader: ExtractTextPlugin.extract("css?sourceMap")},
      {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file'},
      {test: /\.(woff|woff2)$/, loader: 'url?prefix=font/&limit=5000'},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'},
      {test: /\.(jpe?g|png|gif|svg)$/i, loader: "url?limit=10000"},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'},
      {
        test: /favicon\.ico$/,
        loader: 'url',
        query: {
          limit: 1,
          name: '[name].[ext]',
        },
      }
    ]
  }
};

I thought the app.get functions would bring in those gzip files as needed, but I might be missing a step? Also, in my index.html file, do I need to omit the script tag bringing in the bundle file all together?

Any guidance and thoughts would be appreciated!

回答1:

It sounds like you first want to verify if you even are serving the gzipped files, which is easily done through Chrome DevTools.

Open up Chrome DevTools, go to the performance tab and profile your website. After it's loaded, check the network requests section and find the Javascript files you want to verify are being gzipped. If done correctly, you'll see something like this, where the encoded data is the size of the (hopefully compressed) data sent over the wire, and the decoded body is the size of the uncompressed data.

If the two are equal, then you could be gzipping correctly, but not sending it over.

My recommendation to you is to handle this at a reverse proxy level, ideally with the NGINX gzip static module to serve the gzipped files. You really should be using NGINX to serve static content anyway, as it will take load off of your node server.



回答2:

You should put your static listing after the middleware

import express from 'express';
import path from 'path';
import open from 'open';

/* eslint-disable no-console */

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


app.get('*.js', function(req, res, next) {
 req.url = req.url + '.gz';
 res.set('Content-Encoding', 'gzip');
 res.set('Content-Type', 'text/javascript');
 next();
});

app.get('*.css', function(req, res, next) {
 req.url = req.url + '.gz';
 res.set('Content-Encoding', 'gzip');
 res.set('Content-Type', 'text/css');
 next();
});

app.use(express.static('dist'));

app.get('*', function(req, res) {
  res.sendFile(path.join( __dirname, '../dist/index.html'));
});

app.listen(port, function(err) {
  if (err) {
    console.log(err);
  } else {
    open(`http://localhost:${port}`);
  }
});