• 前后端分离+本地服务实时刷新+缓存管理+接口proxy+静态资源增量更新+各种性能优化+上线运维发布——gulp工作流搭建


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

    其实无非就是利用各种gulp插件、node脚本对项目文件做各种IO操作,只是备忘,需要的话,还是自己重新写最合适。 

    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(/
    [	u0020]*?//.+?
    /g,"
    
    "))
    //     .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;])//.+?(?=
    )/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('----
    '+m1+'
    ----
    ');
                var m1_night = m1.replace(/$c-/g, '$c-night-');
                return m1 + '
     @at-root .night &{' + m1_night + '}
    ';
            }))
            .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. 优化迭代了多个版本的比较完美的脚手架

    优点:模块化拆分、回调拉平、功能配置化

    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 ...' + '
    ');
    
    let {
        NODE_ENV: env,
        CDN: cdn
    } = process.env;
    
    // 递归引入gulp/tasks目录下的文件,该操作比较耗时(稍弱些的机器配置 14s左右)
    requireDir('./gulp/tasks', {
        recurse: true
    });
    
    if (env !== 'dev') {
        console.log('
    
    /***********************    打包开始,当前环境为:' + chalk.blue.bold(env) + '    ***********************/' + '
    ');
    }
    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('
    
    /***********************    打包结束,当前环境为:' + chalk.blue.bold(env) + '    ***********************/' + '
    ');
        });
    })
    
    //不做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('
    
    /***********************    打包结束[纯开发包],当前环境为:' + chalk.blue.bold(env) + '    ***********************/' + '
    ');
        });
    })
    
    
    
    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);
    });
  • 相关阅读:
    MPF源码分析之资源文件加载
    oracle存储过程代码日志记录
    fix8源码分析之日志模块
    oracle日期转整数
    记录OCI操作一个诡异的问题
    记录一个虚拟机重启网络启动失败问题
    buff占用内存高
    MFC程序编译链接问题汇总一
    回调函数模型设计
    利用call与apply向函数传递参数
  • 原文地址:https://www.cnblogs.com/youryida/p/5488443.html
Copyright © 2020-2023  润新知