ΠΠ²ΡΠΎΡ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»Π°: Rachit Gulati.
ΠΠ°Π²Π°ΠΉΡΠ΅ ΡΠΆΠΈΠΌΠ°Π΅ΠΌ Π²ΠΌΠ΅ΡΡΠ΅ ΠΈ ΡΠ·Π½Π°Π΅ΠΌ, ΠΊΠ°ΠΊ Brotli ΠΌΠΎΠΆΠ΅Ρ ΠΏΠΎΠΌΠΎΡΡ Π½Π°ΠΌ ΡΠ²Π΅Π»ΠΈΡΠΈΡΡ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ Π½Π°ΡΠΈΡ ΡΠ°ΠΉΡΠΎΠ². Π― ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π» Π΅Π³ΠΎ Π² ΠΎΠ΄Π½ΠΎΠΌ ΠΈΠ· ΠΌΠΎΠΈΡ ΡΠ°Π±ΠΎΡΠΈΡ ΠΏΡΠΎΠ΅ΠΊΡΠΎΠ². ΠΡΠ°ΠΊ, Ρ ΠΏΡΠΎΡΡΠΎ ΠΏΠΎΠ΄ΡΠΌΠ°Π» ΠΏΠΎΠ΄Π΅Π»ΠΈΡΡΡΡ ΡΠ²ΠΎΠΈΠΌ ΠΎΠΏΡΡΠΎΠΌ ΡΠΎ Π²ΡΠ΅ΠΌΠΈ.
ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ π.
ΠΠΊΡΠΏΡΠ΅ΡΡ: ΠΡΡΡΡΡΠΉ, Π½Π΅ΠΎΠΏΠΈΠ½Π½ΠΎΠ²Π°Π½Π½ΡΠΉ, ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΠΈΡΡΡΠΊΠΈΠΉ Π²Π΅Π±-ΠΊΠ°ΡΠΊΠ°Ρ Π΄Π»Ρ Node.js. ΠΡΠΎΡΠΈ: ΠΡΠΎ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΡΠΆΠ°ΡΠΈΡ Π΄Π°Π½Π½ΡΡ Ρ ΠΎΡΠΊΡΡΡΡΠΌ ΠΈΡΡ ΠΎΠ΄Π½ΡΠΌ ΠΊΠΎΠ΄ΠΎΠΌ, ΡΠ°Π·ΡΠ°Π±ΠΎΡΠ°Π½Π½Π°Ρ Jyrki Alakuijala ΠΈ ZoltΓ‘n Szabadka. ΠΠ½ ΠΎΡΠ½ΠΎΠ²Π°Π½ Π½Π° ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΌ Π²Π°ΡΠΈΠ°Π½ΡΠ΅ Π°Π»Π³ΠΎΡΠΈΡΠΌΠ° LZ77, ΠΊΠΎΠ΄ΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π₯Π°ΡΡΠΌΠ°Π½Π° ΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ° 2-Π³ΠΎ ΠΏΠΎΡΡΠ΄ΠΊΠ°. WebPack: ΠΡΠΎ ΠΌΠΎΠ΄ΡΠ»Ρ Bundler. ΠΠ½ ΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅Ρ ΠΌΠΎΠ΄ΡΠ»ΠΈ Ρ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΡΠΌΠΈ ΠΈ Π³Π΅Π½Π΅ΡΠΈΡΡΠ΅Ρ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ Π°ΠΊΡΠΈΠ²Ρ, ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»ΡΡΡΠΈΠ΅ ΡΡΠΈ ΠΌΠΎΠ΄ΡΠ»ΠΈ.
ΠΠ°Π²Π°ΠΉΡΠ΅ Π½Π°ΡΠ½Π΅ΠΌ Ρ ΡΠ΅Π°Π»ΡΠ½ΠΎΠ³ΠΎ Π΄Π΅ΡΡΠΌΠ° π© !!!
ΠΡΠ»ΠΈ Π΄Π²Π° ΡΠΏΠΎΡΠΎΠ±Π° ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ ΡΠΆΠ°ΡΠΈΡ Π² Express Π½Π°ΠΏΡΡΠΌΡΡ (Π±Π΅Π· ΠΊΠ°ΠΊΠΎΠ³ΠΎ-Π»ΠΈΠ±ΠΎ Π²Π΅Π±-ΡΠ΅ΡΠ²Π΅ΡΠ° I.E: Nginx ΠΈ Ρ. Π.):
- Π‘ΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π·Π΄Π°Π½ΠΈΠ΅ ΡΠΆΠ°ΡΡΡ ΡΠ°ΠΉΠ»ΠΎΠ² Ρ ΠΏΠΎΠΌΠΎΡΡΡ WebPack (Π»ΡΠ±Π°Ρ Π΄ΡΡΠ³Π°Ρ Π·Π°Π΄Π°ΡΠ° Frontend ΠΈΠ»ΠΈ Builder) ΠΈ ΠΎΠ±ΡΠ»ΡΠΆΠΈΠ²Π°Π΅Ρ ΠΈΡ ΠΏΠΎ ΡΡΠ΅Π±ΠΎΠ²Π°Π½ΠΈΡ ΠΏΠΎ ΠΌΠ΅ΡΠ΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎΡΡΠΈ ΠΊΠ»ΠΈΠ΅Π½ΡΠ°.
- ΠΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π·Π΄Π°Π½ΠΈΠ΅ ΡΠΆΠ°ΡΡΡ ΡΠ°ΠΉΠ»ΠΎΠ² Π² ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ (Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΡΠ΅Π±ΠΎΠ²Π°ΡΡ (Β«ΡΠΆΠ°ΡΠΈΠ΅Β»)) Π Express Π΄Π»Ρ Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈ ΠΊΠΎΠΌΠΏΡΠ΅ΡΡΠΈΠΎΠ½Π½ΡΡ ΡΠ°ΠΉΠ»ΠΎΠ² ΠΈ ΠΎΠ±ΡΠ»ΡΠΆΠΈΠ²Π°ΠΉΡΠ΅ΡΡ ΠΊΠ»ΠΈΠ΅Π½ΡΡ Π½Π° Π»Π΅ΡΡ. Π― ΡΠΎΠ»ΡΠΊΠΎ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π» ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π·Π΄Π°Π½ΠΈΠ΅ ΡΠ°ΠΉΠ»ΠΎΠ². Π’Π°ΠΊ ΡΡΠΎ Π΄Π°Π²Π°ΠΉΡΠ΅ ΠΏΠΎΠ³ΠΎΠ²ΠΎΡΠΈΠΌ ΠΎΠ± ΡΡΠΎΠΌ Π±ΠΎΠ»Π΅Π΅ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΠΎ.
Π‘ΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ ΡΠΆΠ°ΡΠΈΠ΅ Ρ Express ΠΠ° ΠΏΠ΅ΡΠ²ΠΎΠΌ ΡΠ°Π³Π΅, ΠΊΠΎΡΠΎΡΡΠΉ ΡΡΡΠΎΠΈΡ Π²Π°ΡΠΈ ΠΏΠ°ΠΊΠ΅ΡΡ , Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π²ΠΊΠ»ΡΡΠΈΡΡ ΡΡΠΈ Π΄Π²Π° ΠΏΠ»Π°Π³ΠΈΠ½Π° Π‘ΠΆΠ°ΡΠΈΠ΅ - WebPack-Plugin
ΠΈ brotli-webpack-plugin
ΠΡΠΊΠ°Π·
const CompressionPlugin = require('compression-webpack-plugin'); const BrotliPlugin = require('brotli-webpack-plugin'); module.exports = { plugins: [ new CompressionPlugin({ asset: '[path].gz[query]', algorithm: 'gzip', test: /\.js$|\.css$|\.html$/, threshold: 10240, minRatio: 0.8 }), new BrotliPlugin({ asset: '[path].br[query]', test: /\.js$|\.css$|\.html$/, threshold: 10240, minRatio: 0.8 }) ] }
ΠΡΠΈ ΠΏΠ»Π°Π³ΠΈΠ½Ρ Π±ΡΠ΄ΡΡ Π³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡ ΠΊΠ°ΠΊ gzip, ΡΠ°ΠΊ ΠΈ brotli file Π΄Π»Ρ Π²ΡΠ΅Ρ Π²Π°ΡΠΈΡ ΡΠ²ΡΠ·ΠΊΠΎΠ², Ρ.Π΅. Π΅ΡΠ»ΠΈ ΠΈΠΌΡ ΠΏΡΡΠΊΠ° ‘vendor_d0cfe49e718C136CFE49E718C136C61.js, Π²Ρ ΠΏΠΎΠ»ΡΡΠΈΡΠ΅ Vendor_D0CFE49E718C136C661.js.gzip ΠΈ vendor_d0c661.js.br Π² ΡΠΎΠΌ ΠΆΠ΅ ΠΊΠ°ΡΠ°Π»ΠΎΠ³Π΅ (Π΄Π°Π²Π°ΠΉΡΠ΅ ΠΏΡΠ΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠΈΠΌ, ΡΡΠΎ ΡΡΠΎ /dist/Static/ vendor_d0cfe49e718c1366c661.js. * ΠΠ° Π΄Π°Π½Π½ΡΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ).
PS: ΠΡΠΈΠ²Π΅Π΄Π΅Π½Π½ΡΠΉ Π²ΡΡΠ΅ ΠΊΠΎΠ΄ Π±ΡΠ΄Π΅Ρ Π³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡ ΡΠΎΠ»ΡΠΊΠΎ .gzip ΠΈ .br, Π΅ΡΠ»ΠΈ minratio 0,8 Π΄ΠΎΡΡΠΈΠ³Π°Π΅ΡΡΡ Π²ΠΎ Π²ΡΠ΅ΠΌΡ ΡΠΆΠ°ΡΠΈΡ ΡΠ°ΠΉΠ»ΠΎΠ². Π’Π°ΠΊΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ, Π² ΡΠ»ΡΡΠ°Π΅ ΠΎΡΠ΅Π½Ρ ΠΌΠ°Π»Π΅Π½ΡΠΊΠΈΡ ΡΠ°ΠΉΠ»ΠΎΠ² GZIP ΠΈ BR ΡΠ°ΠΉΠ»Ρ Π½Π΅ Π±ΡΠ΄ΡΡ ΡΠ³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°Π½Ρ. ΠΡΠΈΡΠΈΠ½Π° Π² ΡΠΎΠΌ, ΡΡΠΎ Π²ΡΠ΅ΠΌΡ Π² ΡΠΆΠΈΠΌΠ°Π½ΠΈΠΈ ΠΈ ΡΠ°ΡΠΏΠ°ΡΠΊΠΎΠ²Π°Π½ΠΈΠΈ ΠΊΠΎΡΡΠ»Π΅Π΅Π΅, ΡΠ΅ΠΌ ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ°ΠΉΠ», ΠΎΠ±ΡΠ»ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΠΉ Π±Π΅Π· ΡΠΆΠ°ΡΠΈΡ.
ΠΠ°ΠΌ ΡΠ°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ ΠΏΠΎΡΡΠ΅Π±ΠΎΠ²Π°ΡΡΡΡ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ ΠΏΡΠ±Π»ΠΈΡΠ½ΡΠΉ ΠΏΡΡΡ Π² ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ Π²ΡΡ ΠΎΠ΄Π° WebPack Π² Β«/StaticΒ». ΠΠ½ Π±ΡΠ΄Π΅Ρ ΡΠΊΠ°Π·ΡΠ²Π°Π΅Ρ ΠΏΡΠ±Π»ΠΈΡΠ½ΡΠΉ Π°Π΄ΡΠ΅Ρ URL-Π°Π΄ΡΠ΅ΡΠ° Π²ΡΡ ΠΎΠ΄Π½ΡΡ ΡΠ°ΠΉΠ»ΠΎΠ² ΠΏΡΠΈ ΡΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΠΈ Π² Π±ΡΠ°ΡΠ·Π΅ΡΠ΅. Π§ΡΠΎ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ Π½Π°ΠΌ ΡΠΈΠ»ΡΡΡΠΎΠ²Π°ΡΡ URL-Π°Π΄ΡΠ΅Ρ Π·Π°ΠΏΡΠΎΡΠ° ΠΈ ΠΏΠΎΠ΄Π°Π²Π°ΡΡ ΡΠ°ΠΉΠ»Ρ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Express-Static-Gzip Fonly Static, Π΅ΡΠ»ΠΈ URL ΡΠΎΡΡΠΎΠΈΡ ΠΈΠ· Β«/StaticΒ».
output: { path: '/dist/static', filename: '[name]_[chunkhash].js', chunkFilename: '[id].[chunkhash].js', publicPath: '/static/', },
ΠΠ° Π²ΡΠΎΡΠΎΠΌ ΡΡΠ°ΠΏΠ΅, ΠΊΠΎΡΠΎΡΡΠΉ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠ»ΡΠΆΠΈΡΡ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎΠΌΡ ΡΠ°ΠΉΠ»Ρ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ Π²Ρ ΠΎΠ΄Π½ΡΡ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠ² ΠΈΠ· ΠΊΠ»ΠΈΠ΅Π½ΡΡΠΊΠΎΠ³ΠΎ Π±ΡΠ°ΡΠ·Π΅ΡΠ° ΠΡΠΊΠ°Π· ΠΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Express-Static-Gzip
var express = require("express"); var expressStaticGzip = require("express-static-gzip"); var app = express(); // app.use(express.static(path.join(__dirname))); This was used previously with express. app.use("/", expressStaticGzip(path.join(__dirname), { enableBrotli: true }));
ΠΡΠΈΠ²Π΅Π΄Π΅Π½Π½ΡΠΉ Π²ΡΡΠ΅ ΠΊΠΎΠ΄ ΡΠ²Π»ΡΠ΅ΡΡΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠΎΠΉ ΠΊΠΎΠ΄Π° ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ ΠΈΠ· Β«Express-Static-GzipΒ», Π½ΠΎ Ρ Ρ ΠΎΡΠ΅Π» ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΠΎΠ»ΡΠΊΠΎ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΡΠ°ΠΉΠ»Ρ ΠΈΠ· ΡΡΠΎΠΉ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ. ΠΡΠ»ΠΈ ΡΠ°ΠΉΠ» Π½Π΅ ΡΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ, Ρ Ρ ΠΎΡΠ΅Π» Π±ΡΠΎΡΠΈΡΡ ΠΎΡΠΈΠ±ΠΊΡ, ΠΈ ΠΌΠΎΠΉ ΠΊΠΎΠ΄ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΈΠ΄ΡΠΈ Π΄Π°Π»ΡΡΠ΅ Π½Π° Π΄ΡΡΠ³ΠΈΠ΅ ΠΌΠ°ΡΡΡΡΡΡ. ΠΡΠ°ΠΊ, Ρ ΡΠΎΠ»ΡΠΊΠΎ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ Π²Π·Π»ΠΎΠΌΠ°Π» Π² ΠΈΡΡ ΠΎΠ΄Π½ΠΎΠΌ ΠΊΠΎΠ΄Π΅ ΠΈ ΡΠΎΠ·Π΄Π°Π» Π½ΠΎΠ²ΡΠΉ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΡΠΉ ΡΠ°ΠΉΠ» Compression.js.
ΠΠΈΠΆΠ΅ ΠΏΡΠΈΠ²Π΅Π΄Π΅Π½ Π²Π·Π»ΠΎΠΌΠ°Π½Π½ΡΠΉ ΠΊΠΎΠ΄:
var express = require("express"); const expressStaticGzip = require('compression'); // compression.js gist is available on the github. var app = express(); app.get('*', expressStaticGzip(path.join(__dirname), { urlContains: 'static/', fallthrough: false, enableBrotli: true, }));
ΠΡΡΡ ΡΡΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°, ΠΊΠΎΡΠΎΡΡΠ΅ Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π» Π·Π΄Π΅ΡΡ
1 ** URLContains: ** ΠΡΠΎ ΠΏΡΠΎΠ²Π΅ΡΠΈΡ, Π±ΡΠ΄Π΅Ρ Π»ΠΈ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»ΡΠ½ΡΠΉ URL Π·Π°ΠΏΡΠΎΡΠ° Β«ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ/Β» Π² Π½Π΅ΠΌ. Π’ΠΎΠ³Π΄Π° ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΠ΄Π°Π²Π°ΡΡ ΡΠ°ΠΉΠ»Ρ ΡΠ΅ΡΠ΅Π· ΡΡΠΎΡ ΠΏΠ»Π°Π³ΠΈΠ½, Π΅ΡΠ΅ ΠΈΠ³Π½ΠΎΡΠΈΡΡΠΉΡΠ΅ URL. 2. ΠΠ»ΡΡΡ: ΠΡΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±ΡΡΡ Π»ΠΎΠΆΠ½ΠΎ, ΡΡΠΎΠ±Ρ Π±ΡΠΎΡΠΈΡΡ ΠΎΡΠΈΠ±ΠΊΡ, Π΅ΡΠ»ΠΈ ΡΠ°ΠΉΠ», ΠΊΠΎΡΠΎΡΡΠΉ Π²Ρ ΠΈΡΠ΅ΡΠ΅, Π½Π΅ ΡΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ Π² ΠΊΠ°ΡΠ°Π»ΠΎΠ³Π΅ Path.join (__ dirname), ΠΈ URL-Π°Π΄ΡΠ΅Ρ Β«URLContainsΒ». 3. Enablebrotli: ΠΠ½ ΠΏΡΠΎΠ²Π΅ΡΠΈΡ, Π΄ΠΎΡΡΡΠΏΠ΅Π½ Π»ΠΈ ΡΠ°ΠΉΠ» Brotli Π½Π° Path.join (__ dirname), ΠΈ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠΈΠ΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ Π·Π°ΠΏΡΠ°ΡΠΈΠ²Π°ΡΡΡΡ ΠΊΠ»ΠΈΠ΅Π½ΡΠΎΠΌ, Π·Π°ΡΠ΅ΠΌ ΠΎΠ±ΡΠ»ΡΠΆΠΈΠ²Π°ΡΡ ΡΠ°ΠΉΠ» .br. ΠΠΈΠΆΠ΅ ΠΏΡΠΈΠ²Π΅Π΄Π΅Π½ Π³ΠΈΠ΄ Ρ ΠΊΠΎΠΌΠΏΡΠ΅ΡΡΠΈΠ΅ΠΉ .js. Π― Π²Π·Π»ΠΎΠΌΠ°Π» Ρ Π»ΠΈΠ½ΠΈΠΈ 59-65.
const mime = require('mime'); const serveStatic = require('serve-static'); const fileSystem = require('fs'); module.exports = expressStaticGzip; /** * Generates a middleware function to serve static files. * It is build on top of the express.static middleware. * It extends the express.static middleware with * the capability to serve (previously) gziped files. For this * it asumes, the gziped files are next to the original files. * @param {string} rootFolder: folder to staticly serve files from * @param {{enableBrotli:boolean, * customCompressions:[{encodingName:string,fileExtension:string}], * indexFromEmptyFile:boolean}} options: options to change module behaviour * @returns express middleware function */ function expressStaticGzip(rootFolder, options) { options = options || {}; if (typeof (options.indexFromEmptyFile) === 'undefined') options.indexFromEmptyFile = true; // create a express.static middleware to handle serving files const defaultStatic = serveStatic(rootFolder, options); const compressions = []; const files = {}; // read compressions from options setupCompressions(); // if at least one compression has been added, lookup files if (compressions.length > 0) { findAllCompressionFiles(fileSystem, rootFolder); } return function middleware(req, res, next) { changeUrlFromEmptyToIndexHtml(req); // get browser's' supported encodings const acceptEncoding = req.header('accept-encoding'); // test if any compression is available const matchedFile = files[req.path]; console.log(req.originalUrl, matchedFile); if (matchedFile) { // as long as there is any compression available for this // file, add the Vary Header (used for caching proxies) res.setHeader('Vary', 'Accept-Encoding'); // use the first matching compression to serve a compresed file const compression = findAvailableCompressionForFile(matchedFile.compressions, acceptEncoding); if (compression) { convertToCompressedRequest(req, res, compression); } } // allways call the default static file provider defaultStatic(req, res, (err) => { if (err && (req.originalUrl.indexOf(options.urlContains) > -1)) { console.log('Hola', req.originalUrl, err); return res.status(404).json({ error: `No file found with ${req.originalUrl}` }); } return next(); }); }; /** * Reads the options into a list of available compressions. */ function setupCompressions() { // register all provided compressions if (options.customCompressions && options.customCompressions.length > 0) { for (let i = 0; i < options.customCompressions.length; i += 1) { const customCompression = options.customCompressions[i]; registerCompression(customCompression.encodingName, customCompression.fileExtension); } } // enable brotli compression if (options.enableBrotli) { registerCompression('br', 'br'); } // gzip compression is enabled by default registerCompression('gzip', 'gz'); } /** * Changes the url and adds required headers to serve a compressed file. * @param {Object} req * @param {Object} res */ function convertToCompressedRequest(req, res, compression) { const type = mime.lookup(req.path); const charset = mime.charsets.lookup(type); let search = req.url.split('?').splice(1).join('?'); if (search !== '') { search = `?${search}`; } req.url = req.path + compression.fileExtension + search; res.setHeader('Content-Encoding', compression.encodingName); res.setHeader('Content-Type', type + (charset ? `; charset=${charset}` : '')); } /** * In case it's enabled in the options and the * requested url does not request a specific file, "index.html" will be appended. * @param {Object} req */ function changeUrlFromEmptyToIndexHtml(req) { if (options.indexFromEmptyFile && req.url.endsWith('/')) { req.url += 'index.html'; } } /** * Searches for the first matching compression available from the given compressions. * @param {[Compression]} compressionList * @param {string} acceptedEncoding * @returns */ function findAvailableCompressionForFile(compressionList, acceptedEncoding) { if (acceptedEncoding) { for (let i = 0; i < compressionList.length; i += 1) { if (acceptedEncoding.indexOf(compressionList[i].encodingName) >= 0) { return compressionList[i]; } } } return null; } /** * Picks all files into the matching compression's file list. Search is done recursively! * @param {Object} fs: node.fs * @param {string} folderPath */ function findAllCompressionFiles(fs, folderPath) { const filesMain = fs.readdirSync(folderPath); // iterate all files in the current folder for (let i = 0; i < filesMain.length; i += 1) { const filePath = `${folderPath}/${filesMain[i]}`; const stats = fs.statSync(filePath); if (stats.isDirectory()) { // recursively search folders and append the matching files findAllCompressionFiles(fs, filePath); } else { addAllMatchingCompressionsToFile(filesMain[i], filePath); } } } /** * Takes a filename and checks if there is any compression type matching the file extension. * Adds all matching compressions to the file. * @param {string} fileName * @param {string} fillFilePath */ function addAllMatchingCompressionsToFile(fileName, fullFilePath) { for (let i = 0; i < compressions.length; i += 1) { if (fileName.endsWith(compressions[i].fileExtension)) { addCompressionToFile(fullFilePath, compressions[i]); return; } } } /** * Adds the compression to the file's list of available compressions * @param {string} filePath * @param {Compression} compression */ function addCompressionToFile(filePath, compression) { const srcFilePath = filePath.replace(compression.fileExtension, '').replace(rootFolder, ''); const existingFile = files[srcFilePath]; if (!existingFile) { files[srcFilePath] = { compressions: [compression] }; } else { existingFile.compressions.push(compression); } } /** * Registers a new compression to the module. * @param {string} encodingName * @param {string} fileExtension */ function registerCompression(encodingName, fileExtension) { if (!findCompressionByName(encodingName)) { compressions.push(new Compression(encodingName, fileExtension)); } } /** * Constructor * @param {string} encodingName * @param {string} fileExtension * @returns {encodingName:string, fileExtension:string,files:[Object]} */ function Compression(encodingName, fileExtension) { this.encodingName = encodingName; this.fileExtension = `.${fileExtension}`; } /** * Compression lookup by name. * @param {string} encodingName * @returns {Compression} */ function findCompressionByName(encodingName) { for (let i = 0; i < compressions.length; i += 1) { if (compressions[i].encodingName === encodingName) { return compressions[i]; } } return null; } }
ΠΠ½Π°Π»ΠΈΠ· ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ²:
ΠΠ°Π²Π°ΠΉΡΠ΅ ΡΡΠ°Π²Π½ΠΈΠΌ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΡΠ°ΠΉΡΠ° Ρ Brotli ΠΈΠ»ΠΈ GZIP ΠΈΠ»ΠΈ ΠΏΡΠΎΡΡΠΎ Π½Π΅ΡΠΆΠ°ΡΡΠΌ Π·Π°ΠΌΠΈΠ½ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ.
Π ΠΠΎΠ³Π΅ ΠΌΡ Π΄ΠΎΠ²Π΅ΡΡΠ΅ΠΌ, Π²ΡΠ΅ ΠΎΡΡΠ°Π»ΡΠ½ΡΠ΅ ΠΏΡΠΈΠ½ΠΎΡΡΡ Π΄Π°Π½Π½ΡΠ΅. -W. ΠΠ΄Π²Π°ΡΠ΄Ρ ΠΠ΅ΠΌΠΈΠ½Π³
ΠΠ°Π²Π°ΠΉΡΠ΅ ΠΊΠΎΠΏΠ°ΡΡ β Π² Π°Π½Π°Π»ΠΈΠ· ΠΈ Π½Π°ΠΉΠ΄ΠΈΡΠ΅ ΡΠ΅Π°Π»ΡΠ½ΡΠ΅ ΡΠΈΡΠ»Π°. ΠΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΡΡΠΎ ΠΌΠΎΠ΅ ΡΠ°Π±ΠΎΡΠ΅Π΅ ΠΌΠ΅ΡΡΠΎ Π²Π΅Π±-ΡΠ°ΠΉΡΠ°, ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π»ΠΈ Π΄Π»Ρ Π²Π½ΡΡΡΠ΅Π½Π½Π΅ΠΉ ΡΠ΅Π»ΠΈ. Π― Π½Π΅ ΠΌΠΎΠ³Ρ ΠΏΠΎΠ΄Π΅Π»ΠΈΡΡΡΡ Π½Π°ΡΡΠΎΡΡΠΈΠΌ ΡΠΊΡΠΈΠ½ΡΠΎΡΠΎΠΌ Π·Π΄Π΅ΡΡ, Π½ΠΎ Π½ΠΈΠΆΠ΅ ΡΠ΅Π°Π»ΡΠ½ΡΠ΅ ΡΠΈΡΠ»Π°, ΠΊΠΎΡΠΎΡΡΠ΅ Ρ ΡΠΎΠ±ΠΈΡΠ°Π» ΠΏΡΠΈ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ ΡΠ°ΠΉΡΠ° Π½Π° ΡΠ°Π·Π½ΡΡ ΡΠΈΠΏΠ°Ρ ΠΊΠΎΠΌΠΏΡΠ΅ΡΡΠΈΡΡ .
- Brotli ΡΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ~ 8% ((7.24-6,67)/7.24), ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎ, ΡΠ΅ΠΌ GZIP ΠΈ 65,7% (19,48-6,67)/19,48), ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎ, ΡΠ΅ΠΌ Π½Π΅ΡΠΆΠ°ΡΡΠΉ ΡΠ°ΠΉΠ». ΠΡΠ»ΠΈ Π±ΡΠ°ΡΠ·Π΅Ρ Π½Π΅ ΡΠΌΠΎΠΆΠ΅Ρ ΡΠ»ΡΠΆΠΈΡΡ Brotli, Ρ Π½Π°Ρ Π΅ΡΡΡ Π·Π°Π³ΡΡΠ·Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠΉ Π² GZIP, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ 62% (19,48-7.24)/19,48), ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½Π΅Π΅ Π½Π΅ΡΠΆΠ°ΡΠΎΠ³ΠΎ ΡΠ°ΠΉΠ»Π°. Π’Π°ΠΊ ΡΡΠΎ Π·Π΄Π΅ΡΡ Ρ Π½Π°Ρ Π²ΡΠΈΠ³ΡΠ°ΡΡ ΡΠΈΡΡΠ°ΡΠΈΡ.
- Π’Π΅ΠΏΠ΅ΡΡ Π΄Π°Π²Π°ΠΉΡΠ΅ ΠΏΡΠΎΠ°Π½Π°Π»ΠΈΠ·ΠΈΡΡΠ΅ΠΌ ΡΠ°Π·ΠΌΠ΅Ρ. ΠΡΠΎΡΠΈ – ΡΡΠΎ ((586-458)/586) ~ 21,85% ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎ, ΡΠ΅ΠΌ gzip, ΠΈ ΠΎΠ½ΠΎ Π΅ΡΡΡ ((2.5 1024-458)/2.5 1024) ~ 82,1% ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎ, ΡΠ΅ΠΌ Π½Π΅ΡΠΆΠ°ΡΡΠ΅ ΡΠ°ΠΉΠ»Ρ. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ, ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΠΏΡΡΠΊΠ½ΠΎΠΉ ΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΡ ΡΠ°Π½ΠΈΡΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΡΠΆΠ°ΡΠΈΡ Π±ΡΠΎΡΠ»Π΅ΠΉ. ΠΠ΅ΠΊΠΎΡΠΎΡΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ Π΄Π»Ρ ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎΠΉ ΡΠ΅ΡΠΈ 3G:
Π‘ΠΏΠ°ΡΠΈΠ±ΠΎ Π²ΡΠ΅ΠΌ Π·Π° ΡΡΠ΅Π½ΠΈΠ΅ Π΄ΠΎ ΡΠΈΡ ΠΏΠΎΡ. ΠΡΠ»ΠΈ Π²Π°ΠΌ ΡΡΠΎ Π½ΡΠ°Π²ΠΈΡΡΡ ΠΈ Ρ ΠΎΡΡ, ΡΡΠΎΠ±Ρ Ρ Π½Π°ΠΏΠΈΡΠ°Π» Π±ΠΎΠ»ΡΡΠ΅. ΠΠΎΠΆΠ°Π»ΡΠΉΡΡΠ°, Π΄Π°ΠΉΡΠ΅ ΠΌΠ½Π΅ Π·Π½Π°ΡΡ.