使用 React、Webpack 和 Apache 启用文本压缩

Enable text compression using React, Webpack and Apache

据说the compression-webpack-plugin应该做的。

我用 npm 安装了插件

npm install compression-webpack-plugin --save-dev

并编辑了我的 webpack.config.js 文件以包含

const CompressionPlugin = require('compression-webpack-plugin');

...
  plugins: [
    new CompressionPlugin({
      filename: "[path].gz[query]",
      algorithm: "gzip",
      test: /\.(js|css)$/i,
    }),
...

当我使用 page insights 检查我的网页加载速度时,我的 gz 文件似乎无法识别,或者至少有一个无法识别

这是我的主目录


与我的问题非常相似。我尽量避免使用 .htaccess,因为我在某处听说它不是与 react 和 webpack 一起使用的最佳选择。也许这是错误的?

我尝试使用 kushalvm 的解决方案,但它对我不起作用。

简短回答:kushalvm 的解决方案不完整。为了用gzip/brotli压缩页面大小,有两个步骤:

  1. 在构建时创建 .gz/.br 个文件(或由服务器动态生成)
  2. 提供给他们(而不是 .js 个文件)

并且您正在做第一部分而不是第二部分。因为当你构建一个 React 项目时(在客户端渲染 - CSR 的情况下)你只需创建一个 .html 文件导入一些脚本标签(你的 React 项目)。如果你使用 webpack 的 brotli or compression 插件,你已经创建了一些 .gz/.br 文件,但是 HTML 文件仍然导入旧的 .js 脚本。

那么你可以使用哪些不同的方法来提供你需要配置你的服务器的压缩文件(你不能仅仅通过改变你的 React 配置来做到这一点

解决方案

1)如果你使用的是客户端渲染,你可以创建一个自定义的 express 服务器来提供文件,它非常简单,代码不到 10 行,你可以在 the Official React Docs (with express-static-gzip) 所以您的服务器文件将如下所示:

const express = require('express');
const path = require('path');
const app = express();

app.use(
  expressStaticGzip(path.join(__dirname, 'build'), {
  enableBrotli: true, // only if you have brotli files too
  }),
);

app.use(express.static(path.join(__dirname, 'build')));

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

app.listen(9000);

此服务器将读取构建目录中的文件,如果对某些 js 资产的请求到达,它会检查(如果浏览器支持压缩,以及是否存在压缩文件)然后提供压缩文件

2) 如果您使用 SSR(例如 Next.jsReact-Starter-kit),您可以创建自定义快递服务器,并使用与上述相同的方法。

3) 如果您使用 Apache 网络服务器,您可以像这样使用 Apache gzip/brotli 压缩配置文件:

# enable the rewrite capabilities
RewriteEngine On

# prevents the rule from being overrided by .htaccess files in subdirectories
RewriteOptions InheritDownBefore

# provide a URL-path base (not a file-path base) for any relative paths in the rule's target
RewriteBase /

# GZIP
## allows you to have certain browsers uncompress information on the fly
AddEncoding gzip .gz
## serve gzip .css files if they exist and the client accepts gzip
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.css \.css\.gz [QSA]
## serve gzip .js files if they exist and the client accepts gzip
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.js \.js\.gz [QSA]
## serve gzip .html files if they exist and the client accepts gzip
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.html \.html\.gz [QSA]
## serve correct content types, and prevent mod_deflate double gzip
RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1,E=is_gzip:1]
RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1,E=is_gzip:1]
RewriteRule \.html\.gz$ - [T=text/html,E=no-gzip:1,E=is_gzip:1]
Header set Content-Encoding "gzip" env=is_gzip

4) 如果您使用第三方 JamStack/CDN 提供商,如 Netlify 或 AWS,他们有一些配置供您启用动态 gzip。

您还可以构建时 gzip。这是过程。

  1. 安装Webpack压缩插件

    npm 安装 compression-webpack-plugin --save-dev

  2. 安装并导入插件 var CompressionPlugin = require('compression-webpack-plugin');

  3. 将其添加到插件数组

  plugins: [
    new webpack.DefinePlugin({ 
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    }),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin(),
    new webpack.optimize.AggressiveMergingPlugin(),
    new CompressionPlugin({   <-- Add this
      asset: "[path].gz[query]",
      algorithm: "gzip",
      test: /\.js$|\.css$|\.html$/,
      threshold: 10240,
      minRatio: 0.8
    })
  ]
  1. 最后,将这个中间件添加到 Express 到 return。js.gz 这样你仍然可以从 html 加载 bundle.js 但会收到包。js.gz
app.get('*.js', function (req, res, next) {
  req.url = req.url + '.gz';
  res.set('Content-Encoding', 'gzip');
  next();
});

我也在努力使用我的 React SSR 应用程序提供压缩版本;在我的例子中,扩展名为 js.br 的 brotli 类型 >“bundle.js.br”。以下代码段最终帮助我正确地提供服务。我将开发模式下的 client-side 未压缩包从 1.7 MB 减少到 0.27 MB。试着为你弄明白。提供文件不需要额外的插件。当然压缩版要先用webpack生成。对于 webpack brotli 压缩,我将 npmjs 包 compression-webpack-plugin 与 zlib 结合使用。

var express = require('express');
var app = express();

app.get('*.js', (req, res, next) => {
  if (req.header('Accept-Encoding').includes('br')) {
    req.url = req.url + '.br';
    console.log(req.header('Accept-Encoding'));
    res.set('Content-Encoding', 'br');
    res.set('Content-Type', 'application/javascript; charset=UTF-8');
  }
  next();
});

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

现在让我解释一下上面的代码片段,我将其用于我的 SSR React 应用程序以提供压缩文件。

  • 在开头使用参数“*.js”意味着此 app.get 适用于触发以获取 JS 文件的每个端点。
  • 在回调中,我将 .br 附加到请求的 URL。此外,Content-Encoding 响应 header 设置为 br.
  • 将Content-Typeheader设为application/javascript; charset=UTF-8 指定MIME类型。
  • 最后,非常重要的是,next() 可以继续下一个回调。
  • 不要忘记提供压缩包所在的文件夹。我在我的案例中使用了 app.use(express.static('public'))。