gulp壓縮js排除已壓縮文件

2020-09-30 18:01 更新

gulp默認(rèn)排除語法的弊端

有的時(shí)候我們需要使用 gulp 排除已經(jīng)壓縮過的 js,css 等。如果已壓縮文件是以".min.js"之類命名規(guī)范的還好,如果不是呢?而且還有其他一些場景也會(huì)有需要。
gulp 默認(rèn)支持glob格式,排除語法如下:
舉幾個(gè)例子:
排除目錄

gulp.src([
    baseDir + '/**',                              // Include all
    '!' + baseDir + '/excl1{,/**}',               // Exclude excl1 dir
    '!' + baseDir + '/excl2/**/!(.gitignore)',    // Exclude excl2 dir, except .gitignore
], { dot: true });

排除文件:

gulp.src(['css/**/!(ignore.css)*.css'])
gulp.src([
      '!css/ignnore.css',
      'css/*.css'
    ])

這確實(shí)能夠排除一些文件。但是還不算強(qiáng)大,不能滿足一些復(fù)雜的場景。
而且還存在這樣一個(gè)問題:當(dāng)我有一個(gè) jquery.min.js,在壓縮 js 時(shí),我不想對(duì)它進(jìn)行壓縮,但同時(shí)我又想將其輸出到 dist 目錄下。單純采用上面這種方式需要"曲線救國",場景如下:

gulp.task('jsmin', function (cb) {
        return gulp.src([SRC_DIR+'/**/*.js',,'!'+SRC_DIR+'/**/*.min.js'])
        .pipe(sourcemaps.init())
        .pipe(uglify({mangle: {except: ['require' ,'exports' ,'module' ,'$']}})) //排除混淆關(guān)鍵字
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(DEST_DIR));

});

很遺憾地告訴你, *.min.js 并沒有被拷貝到 DEST_DIR ,當(dāng)然曲線救國可以如下:

gulp.task('copy', () =>{
    return gulp.src([SRC_DIR+'/**/*.min.js'])
        .pipe(gulp.dest(DEST_DIR));
    }
);

但是,這不是很優(yōu)雅。而且上面我們也說了只是采用 glob 語法對(duì)文件進(jìn)行排除不適用于復(fù)雜的場景。

Vinyl文件系統(tǒng)

詳細(xì)說明,請(qǐng)參考 探究Gulp的Stream
現(xiàn)在這里貼出,后面會(huì)說明其目的。
雖然 Gulp 使用的是 Stream,但卻不是普通的 Node Stream,實(shí)際上,Gulp(以及Gulp插件)用的應(yīng)該叫做 Vinyl File Object Stream。
這里的 Vinyl,是一種虛擬文件格式。Vinyl主要用兩個(gè)屬性來描述文件,它們分別是路徑(path)及內(nèi)容(contents)。具體來說,Vinyl并不神秘,它仍然是JavaScript Object。Vinyl官方給了這樣的示例:

var File = require('vinyl');
var coffeeFile = new File({
  cwd: "/",
  base: "/test/",
  path: "/test/file.coffee",
  contents: new Buffer("test = 123")
});

從這段代碼可以看出,Vinyl是Object,path和contents也正是這個(gè)Object的屬性。

解決方案描述:

可以采用gulp插件來實(shí)現(xiàn)更為強(qiáng)大的排除。主要有以下幾個(gè)插件:
gulp-ignore,支持通過 boolean , stat 對(duì)象,函數(shù)來判斷是否排除與包含。判斷函數(shù)接受一個(gè) Vinyl 文件對(duì)象,返回一個(gè) boolean 值。
gulp-filter,支持 glob 模式、函數(shù)過濾,以及過濾后恢復(fù)
gulp-if,支持條件判斷(支持函數(shù)條件)來控制相關(guān)流程。
下面具體說明如何使用

gulp-ignore

API
exclude(condition [, minimatchOptions])
Exclude files whose file.path matches, include everything else
include(condition [, minimatchOptions])
Include files whose file.path matches, exclude everything else
參數(shù)說明:
condition:類型可以為 boolean or stat object or function
當(dāng)為函數(shù)是接受一個(gè) vinyl file 返回 boolean 值

下面貼出官方例子:

var gulpIgnore = require('gulp-ignore');
var uglify = require('gulp-uglify');
var jshint = require('gulp-jshint');

var condition = './gulpfile.js';

gulp.task('task', function() {
  gulp.src('./**/*.js')
    .pipe(jshint())
    .pipe(gulpIgnore.exclude(condition))
    .pipe(uglify())
    .pipe(gulp.dest('./dist/'));
});
var gulpIgnore = require('gulp-ignore');
var uglify = require('gulp-uglify');
var jshint = require('gulp-jshint');

var condition = './public/**.js';

gulp.task('task', function() {
  gulp.src('./**/*.js')
    .pipe(jshint())
    .pipe(gulpIgnore.include(condition))
    .pipe(uglify())
    .pipe(gulp.dest('./dist/'));
});

實(shí)現(xiàn):

var condition = function(f){
    if(f.path.endswith('.min.js')){
        return true;
    }
    return false
};

gulp.task('jsmin', function (cb) {
        return gulp.src([SRC_DIR+'/**/*.js'])
        .pipe(gulpIgnore.exclude(condition))
        .pipe(sourcemaps.init())
        .pipe(uglify({mangle: {except: ['require' ,'exports' ,'module' ,'$']}})) //排除混淆關(guān)鍵字
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(DEST_DIR));

});

優(yōu)點(diǎn):很明顯,你可以在條件函數(shù)中做你想做的任何事情,包括使用正則表達(dá)式等。
缺點(diǎn):和默認(rèn)情況一樣,不支持被排除文件的拷貝

gulp-if

和 gulp-ignore 作者是同一個(gè)人
API:
gulpif(condition, stream [, elseStream, [, minimatchOptions]])
gulp-if will pipe data to stream whenever condition is truthy.
If condition is falsey and elseStream is passed, data will pipe to elseStream
這就類似于三目運(yùn)算符,功能用偽代碼表示就是if condition then stream(data) else elseStream(data)
After data is piped to stream or elseStream or neither, data is piped down-stream.

參數(shù)說明:
condition 與gulp-ignore一致。
Type: boolean or stat object or function that takes in a vinyl file and returns a boolean or RegularExpression that works on the file.path

stream
Stream for gulp-if to pipe data into when condition is truthy.

elseStream
Optional, Stream for gulp-if to pipe data into when condition is falsey.

minimatchOptions
Optional, if it's a glob condition, these options are passed to minimatch.

官方例子:

var gulpif = require('gulp-if');
var uglify = require('gulp-uglify');

var condition = true; // TODO: add business logic

gulp.task('task', function() {
  gulp.src('./src/*.js')
    .pipe(gulpif(condition, uglify()))
    .pipe(gulp.dest('./dist/'));
});

Only uglify the content if the condition is true, but send all the files to the dist folder
僅壓縮符合條件的文件,丹斯所有文件(包括不符合條件的)都會(huì)被發(fā)送到 dist 目錄。

實(shí)現(xiàn):

var condition = function(f){
    if(f.path.endswith('.min.js')){
        return false;
    }
    return true;
};

gulp.task('jsmin', function (cb) {
        return gulp.src([SRC_DIR+'/**/*.js'])
        .pipe(sourcemaps.init())
        .pipe(gulpif(condition, uglify({mangle: {except: ['require' ,'exports' ,'module' ,'$']}}))) //排除混淆關(guān)鍵字
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(DEST_DIR));

});

gulp-filter

API
filter(pattern, [options])
Returns a transform stream with a .restore object.
參數(shù)說明:
pattern
Type: string, array, function
Accepts a string/array with globbing patterns which are run through multimatch.
如果是函數(shù),則應(yīng)該接受一個(gè) vinyl file object作為第一個(gè)參數(shù),return true/false whether to include the file:

filter(file => /unicorns/.test(file.path));

選項(xiàng)options
Type: object
Accepts minimatch options.
Note: Set dot: true if you need to match files prefixed with a dot (eg. .gitignore).

options.restore
Type: boolean
Default: false
Restore filtered files.

options.passthrough
Type: boolean
Default: true

When set to true filtered files are restored with a PassThrough stream, otherwise, when set to false, filtered files are restored as a Readable stream.

官方例子:

const gulp = require('gulp');
const uglify = require('gulp-uglify');
const filter = require('gulp-filter');

gulp.task('default', () => {
    // create filter instance inside task function
    const f = filter(['**', '!*src/vendor'], {restore: true});

    return gulp.src('src/**/*.js')
        // filter a subset of the files
        .pipe(f)
        // run them through a plugin
        .pipe(uglify())
        // bring back the previously filtered out files (optional)
        .pipe(f.restore)
        .pipe(gulp.dest('dist'));
});
gulp.task('defaultmulti', () => {
    const jsFilter = filter('**/*.js', {restore: true});
    const lessFilter = filter('**/*.less', {restore: true});

    return gulp.src('assets/**')
        .pipe(jsFilter)
        .pipe(concat('bundle.js'))
        .pipe(jsFilter.restore)
        .pipe(lessFilter)
        .pipe(less())
        .pipe(lessFilter.restore)
        .pipe(gulp.dest('out/'));
});

實(shí)戰(zhàn):

var condition = function(f){
        if(f.path.endswith('.min.js')){
            return false;
        }
        return true;
    };
const jsFilter = filter(condition, {restore: true});
   
    gulp.task('jsmin', function (cb) {
            return gulp.src([SRC_DIR+'/**/*.js'])
            .pipe(jsFilter)
            .pipe(sourcemaps.init())
            .pipe(uglify({mangle: {except: ['require' ,'exports' ,'module' ,'$']}})) //排除混淆關(guān)鍵字
            .pipe(sourcemaps.write('.'))
            .pipe(jsFilter.restore)
            .pipe(gulp.dest(DEST_DIR));
    
    });

部分文件被壓縮,所有文件都會(huì)被發(fā)送到 DEST_DIR

小結(jié)

如何選擇?
gulp-if:可以完美解決問題,且足夠優(yōu)雅。
gulp-filter:可以完美解決問題,但是相比gulp-if略顯麻煩。更復(fù)雜的場景下用gulp-filter比gulp-if方便。
gulp-ignore:更為強(qiáng)大的過濾,在僅僅只是過濾而不拷貝的情況下首推。

原文鏈接:gulp排除已壓縮文件思路

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)