先後端分離+本地服務實時刷新+緩存管理+接口proxy+靜態資源增量更新+各類性能優化+上線運維發佈——gulp工做流搭建

技巧集:http://www.gulpjs.com.cn/docs/recipes/css

其實無非就是利用各類gulp插件、node腳本對項目文件作各類IO操做,只是備忘,須要的話,仍是本身從新寫最合適。 html

1. 一個justwork的多頁應用工做流

毛病:若是須要task之間的同步依賴關係,那麼上一個task function裏面須要有return;各個task的依賴,應該用gulpSequence作拉平。前端

var gulp = require('gulp'),
    sass = require('gulp-sass'),
    postcss = require('gulp-postcss'),
    autoprefixer = require('autoprefixer'),
    browserSync = require('browser-sync').create(),
    open = require('gulp-open'),
    del = require('del'),
    usemin = require('gulp-usemin'),
    // useref = require('gulp-useref'),// 這個相對目錄有問題  媽的 不用了
    zip = require('gulp-zip'),
    gulpRevAll = require('gulp-rev-all'),
    revReplace = require("gulp-rev-replace"),
    uglify = require('gulp-uglify'),
    minifyCss = require('gulp-minify-css'),

    Replace = require('gulp-replace'),
    htmlmin = require('gulp-htmlmin'),
    gulpSequence = require('gulp-sequence'),
    argv = require('yargs').argv; // node命令傳入的參數整合參數




/**************************配置項**************************/
var _opt = {
    src: 'app/',
    // src: 'dist/sce/app/',
    // devHost:"sns-operating-dev.sohuno.com",
    // devHost: "dev.w.sohu.com", //默認當前局域網ip
    // startPath: '/view/intro.html',
    startPath: '/view/index.html',

    dest: 'dist/',
    tem: 'tem/',
    revDelay: 5, //單位秒
    staticInCDN: true, //默認放cdn
    cdnPath: '//sns_business.cdn.sohusce.com/square-unlogin-v3/',
    // staticProxyPath: '/h5static/',//須要代理的靜態資源路徑標記
    sce: {
        // releaseUrl: 'http://fe.w.sohu.com/h5apps/sns-square-test/view/intro.html', //線上或測試環境的項目url
        manageUrl: '//console.sce.sohuno.com/apps/versions?appid=',
        path: 'sce/',
        yamlPath: 'sce/app.yaml',
        appPath: 'sce/app/',
        confPath: 'sce/conf/',
        test: { //暫僅測試環境 這裏須要優化配置 gulp發佈的時候帶上環境參數 如gulp r --test/product
            appId: 212250773,
            sceHost: '//sns-square-unlogin-v30-test.sce.sohuno.com',
            backendHost: {
                'sns-core': 'sns-api.apps.sohuno.com', //sns-data-api-test.sce.sohuno.com sns-api.apps.sohuno.com msapi.t.sohu.com
                // 'sns-data':'sns-data-api-test.sce.sohuno.com'
            }
        },
        product: {
            appId: 985464923,
            sceHost: '//sns-square-unlogin-v30.sce.sohuno.com',
            backendHost: {
                'sns-core': 'sns-api.apps.sohuno.com',
                // 'sns-data':'sns-data.sohuno.com'
            }
        }
    }
};

/**************************本地開發Server**************************/
var _env = _opt.sce[Object.keys(argv)[1]] ? Object.keys(argv)[1] : 'test';
console.log("● serverPath:" + _opt.src);
console.log("● serverEnv:" + _env);

// 靜態服務器
gulp.task('serve', ['sass'], function() {
    var proxyMiddleware = require('http-proxy-middleware');
    var proxy = proxyMiddleware(['/sns-core'], {
        target: 'http://' + _opt.sce[_env].backendHost['sns-core'],
        pathRewrite: {
            '^/sns-core/(.*)': '/$1'
        },
        changeOrigin: true,
        headers: {
            host: _opt.sce[_env].backendHost['sns-core'],
            origin: 'http://' + _opt.sce[_env].backendHost['sns-core']
        }
    });


    //禁止開發server的緩存
    var proxyNoCache = function(req, res, next) {
        res.setHeader('CacheControl', 'no-cache');
        res.setHeader('Pragma', 'no-cache');
        res.setHeader('Expires', '-1');
        res.setHeader('ETag', Date.now()); //禁止304
        next();
    }

    browserSync.init({
        server: {
            baseDir: _opt.src //_opt.dest//''//''// _opt.dest,//_opt.dest src
        },
        ui: {
            port: 3030
        },
        host: _opt.devHost, //hosts文件設置代理到開發機IP server種cookie用
        open: "external", //
        startPath: _opt.startPath,
        ghostMode: false, //關掉多設備同步
        middleware: [proxy, proxyNoCache]
    });

    gulp.watch(_opt.src + "/static/scss/*.scss", ['sass']);
    gulp.watch([_opt.src + "/static/js/**",
        _opt.src + "/static/img/**",
        _opt.src + "/**/*.html"
    ]).on('change', browserSync.reload);
});


// gulp.task('rrr', function() {
//     return gulp.src([_opt.src + "/static/scss/sns-base.scss"], {
//             base: _opt.src + "/static/scss/"
//         })
//             .pipe(Replace(/\n[\t\u0020]*?\/\/.+?\n/g,"\n\n"))
//     .pipe(gulp.dest(_opt.src + "/static/acss/"))
//     });

gulp.task('sass', function() {
    var postPlugins = [
        autoprefixer() //browserlist read from package.json //取值https://github.com/ai/browserslist#queries
    ];
    return gulp.src([_opt.src + "/static/scss/**/*.scss"], {
            base: _opt.src + "/static/scss/"
        })
        // auto append night mode(.night),color base scss/_const.scss
        // 之後抽象成一個插件
        // 最好的實現不是預先刪除註釋,而應該是不去替換註釋裏的夜色纔對 @20180119
        // 有個bug 啓動的時候能夠注掉 修改scss後的reload任務很差使
        // 幹掉scss中的註釋 避免下面的夜色模式替換 把註釋裏的也給替換了致使scss結構錯亂報錯
        .pipe(Replace(/([\s;])\/\/.+?(?=\n)/g, function(s, m1) {
            //刪掉空字符或者;後面的單行註釋內容;
            //這個正則引擎 不支持確定逆序表達式 (?<=[\s;])  只能function return m1了
            return m1;
        }))
        .pipe(Replace(/\/\*.+?\*\//g,''))//刪掉塊級註釋  咦尼瑪 這個怎麼很差使 見main.css
        //_const.scss中色值變量命名 需遵循規範 $c-grey1:#000; $c-night-grey1:#454545;
        .pipe(Replace(/([\w-]+?\s*?\:.*\$c-.+?;)/g, function(s, m1, m2) {
            // console.log('----\r\n'+m1+'\r\n----\r\n');
            var m1_night = m1.replace(/\$c-/g, '$c-night-');
            return m1 + '\n @at-root .night &{' + m1_night + '}\n';
        }))
        .pipe(sass())
        .on('error', function(err) {
            console.log('Less Error!', err.message);
            this.emit('end');
        })
        .pipe(postcss(postPlugins))
        .pipe(gulp.dest(_opt.src + "/static/css/"))
        .pipe(browserSync.reload({ stream: true }));
});

// gulp.task('openUrl', function() {
//     console.log('----打開sce線上url,fiddler代理至本地');
//     gulp.src(__filename)
//         .pipe(open({ uri: _opt.sce.releaseUrl, app: "chrome" }));
// });

/**************************臨時工具處理**************************/
//圖片壓縮 非尺寸
gulp.task('imgmin1', function() {
    var imagemin = require('gulp-imagemin');
    // gulp.src(_opt.src+'/img/*.+(png)')
    gulp.src(_opt.src + '/static/img/**/*.png', {
            base: _opt.src
        })
        .pipe(imagemin())
        .pipe(gulp.dest(_opt.src));
});


/**************************打包發佈正式方案**************************/

gulp.task('delDest', function() {
    // console.log("● 環境:"+_env);
    var path = _opt.dest;
    console.log('----清空目錄' + path);
    return del(path);
});
gulp.task('tem', ['delDest'], function() {
    var path = _opt.tem;
    console.log('----清空目錄' + path);
    return del(path);
});

gulp.task('delTem', function() {
    var path = _opt.tem;
    console.log('----清空目錄' + path);
    return del(path);
});
gulp.task('delSceApp', function() {
    var path = _opt.sce.appPath;
    console.log('----清空目錄' + path);
    return del(path);
});
// 拷貝文件
gulp.task('copy2tem', ['delDest', 'delTem', 'sass'], function() {
    console.log('----拷貝文件到臨時文件夾');
    return gulp.src([
            _opt.src + '/static/**',
            _opt.src + '/view/**',
            '!' + _opt.src + '/static/scss/**'
        ], {
            base: _opt.src
        })
        .pipe(gulp.dest(_opt.tem));
});
// 合併
gulp.task('usemin', ['copy2tem'], function() { //
    return gulp.src([
            _opt.tem + 'view/**/*.html'
        ], {
            base: _opt.tem
        })
        .pipe(usemin({ //成功的先不上報處理了
            jsAttributes: {
                // onload: 'window.srcLoadLog && srcLoadLog(this)',
                onerror: 'window.srcErrorLog && srcErrorLog(this)'
            },
            cssAttributes: {
                // onload: 'window.srcLoadLog && srcLoadLog(this)',
                onerror: 'window.srcErrorLog && srcErrorLog(this)'
            }

        }))
        .pipe(gulp.dest(_opt.tem));
});

//js壓縮
gulp.task('jsmin', ['usemin'], function() {
    console.log('----壓縮js');
    return gulp.src([
            _opt.tem + '/static/js/**/*.js',
            '!' + _opt.tem + '/static/js/**/*.min.js'
        ], {
            base: _opt.tem
        })
        // .pipe(uglify())
        .pipe(uglify({
            compress: {
                // "quote-keys":true//ie8
                // properties:false,
                drop_console: true
            }
            // quote_keys: true    
            // beautify : { beautify: false, ascii_only: true, quote_keys: true }    
        }))
        .pipe(gulp.dest(_opt.tem));
});
//css壓縮
gulp.task('cssmin', ['usemin'], function() {
    console.log('----壓縮CSS');
    return gulp.src([
            _opt.tem + '/static/css/**/*.css',
            '!' + _opt.tem + '/static/css/*.min.css'
        ], {
            base: _opt.tem
        })
        .pipe(minifyCss())
        .pipe(gulp.dest(_opt.tem));
});

// 加MD5戳
gulp.task('revAll', ['jsmin', 'cssmin'], function() { //
    console.log('----加MD5戳&替換相互引用(延時' + _opt.revDelay + 's後進行下一任務)');
    // var revAll = new RevAll({
    //     dontRenameFile: [/^\/static\/img\/ad-avatar/]//ad-avatar前端不直接用 手動替換後上傳cdn給server下發用
    //     // ,dontGlobal:[]
    //     // ,dontSearchFile:[/^\/static\/js\//]//dontSearchFile 對js/xx/下的路徑文件 不作內容中的引用替換處理  
    // });

    gulp.src([_opt.tem + '/*/**'], { ///static/**
            base: _opt.tem
        })
        .pipe(gulpRevAll.revision({
            dontRenameFile: [/^\/view\//, /^\/static\/img\/ad-avatar/] //ad-avatar前端不直接用 手動替換後上傳cdn給server下發用
                // ,dontGlobal:[]
                ,
            dontSearchFile: [/^\/static\/js\//] //dontSearchFile 對js/xx/下的路徑文件 不作內容中的引用替換處理  
        }))
        .pipe(gulp.dest(_opt.tem))
        .pipe(gulpRevAll.versionFile())
        .pipe(gulp.dest(_opt.tem))
        .pipe(gulpRevAll.manifestFile())
        .pipe(gulp.dest(_opt.tem));
});

//htmlmin
gulp.task('htmlmin', function() { //
    console.log('----htmlmin');
    return gulp.src([
            _opt.tem + '/**/*.html' // '!'+_opt.dest+'/tpls/write/write.html' 
        ], {
            base: _opt.tem
        })
        .pipe(htmlmin({
            collapseWhitespace: true,
            removeComments: true,
            minifyCSS: true,
            minifyJS: true
        }))
        .pipe(gulp.dest(_opt.tem + '/'));
});
//加cdn前綴
gulp.task('cdnPre', ['htmlmin'], function() {
    if (!_opt.staticInCDN) return false;
    console.log('----加CDN前綴');
    return gulp.src([
            _opt.tem + '/**/*.html'
            // ,_opt.dest + '/static/js/common/myLib.*.js'
        ], {
            base: _opt.tem
        }) //下面往後寫成通配符嚴格匹配 path/**.xxx 上面的js就能夠寫成all js了
        .pipe(Replace(/"[./]*static\//g, '"' + _opt.cdnPath)) //有點風險 往後優化
        .pipe(gulp.dest(_opt.tem + '/'));
});
//imgmin - 這個任務時間太長 依賴創建不起來,r完了還會在壓着,因此從gulp 抽成單獨一項任務,gulp serve前手動搞
// gulp.task('imgmin',['copy2tem'],  function() {
//     var imagemin = require('gulp-imagemin');
//     // gulp.src(_opt.src+'/img/*.+(png)')
//     gulp.src(_opt.tem + '/static/img/**/*.png', {
//             base: _opt.tem
//         })
//         .pipe(imagemin())
//         .pipe(gulp.dest(_opt.tem));
// });


//拷貝sce基礎文件至dist
gulp.task('sce_base2dist', function() {
    console.log('----拷貝sce基礎文件到發佈目錄');
    return gulp.src([
            _opt.sce.path + "/**"
        ], {
            base: _opt.sce.path
        })
        .pipe(gulp.dest(_opt.dest + "/sce"));
});
// 根據環境修改sce appId
gulp.task('sceConfigInit', ['sce_base2dist'], function() {
    console.log('----sce配置初始化');
    // return gulp.src(_opt.sce.yamlPath)
    return gulp.src([
            _opt.dest + _opt.sce.yamlPath,
            _opt.dest + _opt.sce.confPath + '/nginx_server.inc'
        ], {
            base: _opt.dest
        }) //backendHost  下面正則優化 加強匹配的嚴謹性--這裏之後改爲寫入而不是 替換是否是能好一些
        .pipe(Replace(/appid: \d+/, 'appid: ' + _opt.sce[_env].appId))
        .pipe(Replace(/server_sns-core(?=[;/ ])/g, _opt.sce[_env].backendHost['sns-core']))
        .pipe(gulp.dest(_opt.dest));
});


// 拷貝文件
gulp.task('tem2sceDist', ['cdnPre'], function() {
    console.log('----拷貝項目文件到dist/sce目錄');
    return gulp.src([
            _opt.tem + "/**",
            '!' + _opt.tem + 'rev-*.json'
        ], {
            base: _opt.tem
        })
        .pipe(gulp.dest(_opt.dest + _opt.sce.appPath));
});
// 靜態資源壓縮zip for cdn
gulp.task('static2zip', ['cdnPre'], function() {
    if (!_opt.staticInCDN) return false;
    console.log('----靜態資源to zip for cdn');
    var cdn_project_name = 'toCDN_' + _opt.cdnPath.match(/\/\/(.+?)\.cdn/)[1] + '_' + _opt.cdnPath.replace(/.+\.com\//, "").replace(/\//g, "") + ".zip";
    // var cdn_project_name = 'toCDN_' + _opt.staticProxyPath.replace(/\//g, "") + ".zip";
    return gulp.src([
            _opt.tem + '/static/**'
        ], {
            base: _opt.tem + '/static/'
        })
        .pipe(zip(cdn_project_name))
        .pipe(gulp.dest(_opt.dest)); //剔除rev map文件、zip打包文件  須要優化
});

// sce項目zip
gulp.task('sce2zip', ['tem2sceDist', 'sceConfigInit'], function() {
    console.log('----sce to zip');
    var zip_name = 'sce_' + _opt.sce[_env].appId + '.zip';
    return gulp.src([_opt.dest + _opt.sce.path + '/**'])
        .pipe(zip(zip_name))
        .pipe(gulp.dest(_opt.dest));
});

//發佈
gulp.task('packContinue', ['sce2zip', 'static2zip'], function() {
    console.log('----清除臨時文件');
    // 清除臨時文件
    del(_opt.tem);
    console.log('● 環境:' + _env);
    console.log('● 前端項目已發佈到' + _opt.dest);
    if (_opt.staticInCDN) console.log('○ 靜態資源zip請上傳至:' + _opt.cdnPath);
    console.log('○ sce zip請上傳至:' + _opt.sce.manageUrl + _opt.sce[_env].appId);
    console.log('● sce前端服務host:' + _opt.sce[_env].sceHost);

    console.log('----完成!');
});
// 發佈-cdn
gulp.task('r', ['revAll'], function(cb) {
    // console.log("----環境:"+_env);
    setTimeout(function() { //延時太挫 待優化 https://github.com/gulpjs/gulp/issues/96
        gulpSequence('packContinue', cb);
    }, _opt.revDelay * 1000); //revAll 延時
});
// 發佈-local
gulp.task('r:local', ['revAll'], function(cb) {
    _opt.staticInCDN = false;
    // console.log("----環境:"+_env);
    setTimeout(function() { //延時太挫 待優化 https://github.com/gulpjs/gulp/issues/96
        gulpSequence('packContinue', cb);
    }, _opt.revDelay * 1000); //revAll 延時
});

 

2. 優化迭代了多個版本的比較完美的腳手架

優勢:模塊化拆分、回調拉平、功能配置化node

let gulp = require('gulp'),
    config = require('./config.js'),
    del = require('del'),
    requireDir = require('require-dir'),
    chalk = require('chalk'),
    runSequence = require('run-sequence');

console.log('Waiting For Gulp Tasks Loading ...' + '\n');

let {
    NODE_ENV: env,
    CDN: cdn
} = process.env;

// 遞歸引入gulp/tasks目錄下的文件,該操做比較耗時(稍弱些的機器配置 14s左右)
requireDir('./gulp/tasks', {
    recurse: true
});

if (env !== 'dev') {
    console.log('\n\n/***********************    打包開始,當前環境爲:' + chalk.blue.bold(env) + '    ***********************/' + '\n');
}
gulp.task('build', function(cb) { //默認不放 CDN
    del.sync(config.temp);
    del.sync(config.dist);
    runSequence(['svg'], ['eslint', 'ES6', 'imgMin', 'fileInclude', 'sceInit'], ['sass'], ['uncss'], ['sprite'],['jscss2dist'], ['usemin'], ['cssmin', 'jsmin', 'htmlmin'], ['revision'], ['cdnPre'], ['delRedundant'], ['zip'], ['openUrl'], function() {
        if (cdn === 'true') console.log('○ 靜態資源zip請上傳至:' + config.cdnPath);
        console.log('○ sce zip請上傳至:' + config.sce.manageUrl + config.sce[env].appId);
        console.log('\n\n/***********************    打包結束,當前環境爲:' + chalk.blue.bold(env) + '    ***********************/' + '\n');
    });
})

//不作jscss合併等工做 直接打包
gulp.task('build:dev', function(cb) { 
    del.sync(config.temp);
    del.sync(config.dist);
    runSequence(['svg'], ['eslint', 'ES6', 'imgMin', 'fileInclude', 'sceInit'], ['sass'], ['sprite'],'temp2dist', ['zip'], ['openUrl'], function() {
        if (cdn === 'true') console.log('○ 靜態資源zip請上傳至:' + config.cdnPath);
        console.log('○ sce zip請上傳至:' + config.sce.manageUrl + config.sce[env].appId);
        console.log('\n\n/***********************    打包結束[純開發包],當前環境爲:' + chalk.blue.bold(env) + '    ***********************/' + '\n');
    });
})



gulp.task('dev', function(cb) {
    del.sync(config.temp);
    config.buryPointSwitch = false;
    config.isImgOptmize = false;
    runSequence(['svg'], ['fileInclude', 'eslint', 'ES6', 'imgMin'], ['sass'], ['sprite'], ['dev-server'], cb);
});

//打包到dist 且啓動目錄改到dist(用來本地測試打包後的頁面運行 注:須要去dev-server改一下serverPath)
gulp.task('dev:dist', function(cb) {
    del.sync(config.temp);
    config.buryPointSwitch = false;
    config.isImgOptmize = false;
    // runSequence(['svg'], ['eslint', 'ES6', 'imgMin', 'fileInclude', 'sceInit'], ['sass'], ['sprite'],'temp2dist', ['dev-server'], cb);
    runSequence( ['delRedundant'], ['svg'], ['eslint', 'ES6', 'imgMin', 'fileInclude', 'sceInit'], ['sass'], ['uncss'], ['sprite'], ['usemin'], ['cssmin', 'jsmin', 'htmlmin'], ['revision'],'dev-server', cb);
});
相關文章
相關標籤/搜索