减小资源大小的过程叫做资源压缩。针对不同类型的资源有不同的压缩技术。本文主要总结文本资源的压缩。即我们网页上面的代码文本如JS、CSS等。
代码文本里边有许多对于运行没有作用的部分,如多余的空白,注释,我们在生产环境中可以将它们去掉来减少网络传输字节。
const gulp = require('gulp');
const uglify = require('gulp-uglify');
const babel = require('gulp-babel');
const gutil = require('gulp-util');
gulp.task('script', function() {
gulp.src('src/*.js')
.pipe(babel({
presets: ['env']
}))
.pipe(uglify())
.on('error', err=> {
gutil.log(gutil.colors.red('[Error]'), err.toString());
})
.pipe(gulp.dest('dist'))
});
以src/script.js为例:
// script1
const a = 3; //a
const b = 4; // b
const c = 5; // c
const arr1 = [a,b,c];
for(let item of arr1){ //遍历arr数组
console.log(item); //打印每一项
}
// 测试文件压缩方案。
// 测试修改
进行babel编译后,如果不压缩,大小为805字节,压缩后为468字节。gulp-uglify将所有代码压缩到一行,去除所有空格,注释。
源代码和编译后的代码或者是压缩后的代码差别比较大,也难以阅读,调试最终代码就变得很困难,可以使用sourcemap解决,还是以gulp为例,改写上面的gulpfile.js:
gulp.task('script', function() {
gulp.src('src/*.js')
.pipe(sourcemaps.init())
.pipe(babel({
presets: ['env']
}))
.pipe(uglify())
.on('error', err=> {
gutil.log(gutil.colors.red('[Error]'), err.toString());
})
.pipe(sourcemaps.write('./maps'))
.pipe(gulp.dest('dist'))
});
以gulp为例,gulp-minify-css会去除css中多余的空格、注释,还会将相同选择器的规则进行合并:
gulp.task('style',()=>{
gulp.src('css/*.css')
.pipe(minifyCSS())
.pipe(gulp.dest('dist/css'))
});
压缩前:
html,body {
width: 100%;
height: 100%;
}
/*盒子相关*/
#red {
width: 40px;
height: 40px;
background-color: red;
}
/*字体相关*/
#red {
font-size: 16px;
font-weight: bold;
}
压缩后:
body,html{width:100%;height:100%}#red{width:40px;height:40px;background-color:red;font-size:16px;font-weight:700}
gzip是很常用的Web资源压缩方案,以Node为例:
const gzip = require('zlib').createGzip();
const fs = require('fs');
const path = require('path');
const inp = fs.createReadStream(path.join(__dirname,'./file/test.txt')); //830字节
const outp = fs.createWriteStream(path.join(__dirname,'./file/test.txt.gzip')); //345字节
inp.pipe(gzip).pipe(outp);
详细API见: https://nodejs.org/dist/latest-v8.x/docs/api/zlib.html
在express中使用Gzip压缩:
const compression = require('compression')
const express = require('express')
const app = express()
// compress all responses
app.use(compression())
为了选择要采用的压缩算法,浏览器和服务器之间会使用主动协商机制。
客户端请求头:Accept-Encoding: xxx,xxx指明支持的压缩算法清单和优先级。
服务端�响应头:Content-Encoding: xxx指明使用的压缩算法。
来自Node.js官网例子:
// client request example
const zlib = require('zlib');
const http = require('http');
const fs = require('fs');
const request = http.get({ host: 'example.com',
path: '/',
port: 80,
headers: { 'Accept-Encoding': 'gzip,deflate' } });
request.on('response', (response) => {
const output = fs.createWriteStream('example.com_index.html');
switch (response.headers['content-encoding']) {
// or, just use zlib.createUnzip() to handle both cases
case 'gzip':
response.pipe(zlib.createGunzip()).pipe(output);
break;
case 'deflate':
response.pipe(zlib.createInflate()).pipe(output);
break;
default:
response.pipe(output);
break;
}
});
// server example
// Running a gzip operation on every request is quite expensive.
// It would be much more efficient to cache the compressed buffer.
const zlib = require('zlib');
const http = require('http');
const fs = require('fs');
http.createServer((request, response) => {
const raw = fs.createReadStream('index.html');
let acceptEncoding = request.headers['accept-encoding'];
if (!acceptEncoding) {
acceptEncoding = '';
}
// Note: This is not a conformant accept-encoding parser.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
if (/\bdeflate\b/.test(acceptEncoding)) {
response.writeHead(200, { 'Content-Encoding': 'deflate' });
raw.pipe(zlib.createDeflate()).pipe(response);
} else if (/\bgzip\b/.test(acceptEncoding)) {
response.writeHead(200, { 'Content-Encoding': 'gzip' });
raw.pipe(zlib.createGzip()).pipe(response);
} else {
response.writeHead(200, {});
raw.pipe(response);
}
}).listen(1337);