最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 超详细Gulp打包Seajs模块压缩合并,绝对对你 '老' 项目有帮助 ~

    正文概述 掘金(部长Jeddy)   2021-03-17   836

    前言

    • 项目背景:
      • 项目存在于n+1年前的php旧框架中,其中使用的技术大家不用猜应该也知道了;
      • 由于其中许多业务一直都存在这个php旧框架中,而且暂时性的不存在做整体迁移的情况,所以只能在php旧框架中动手了 [? 伤心太平洋];
    • 现阶段需解决的点:
      • php旧框架版本戳问题 [? 暗自伤心]:由于浏览器缓存机制,每次修改文件,需手动更改对应的版本戳,修改成本较高,涉及文件变更也较多;
      • ES6语法支持问题 [? 猛男落泪]:了解过javascript的同学应该知道,ES6语法可以更精简你的代码,从而提升你的开发效率;
      • js、css 文件未压缩 [? 用户难受]
      • 由于我们都是有追求技术的Team,同时也秉承用户至上的理念,所以为了解决上述问题,我们的技术革命刻不容缓;

    技术调研

    • Grunt VS Gulp VS Webapck
    • Grunt:
      • 配置与生态没有达到我们的要求
    • Webapck:
      • 不支持CMD模式,需下载对应的转换成AMD或CommonJs的插件
      • 由于项目的seajs写法没有统一性,故可能存在转换后踩坑的情况;
    • Gulp:
      • 与seajs的生态配合较好;
      • 针对seajs打包的一系列流程与插件都有实例;
      • 与其它项目的打包构建一致性;
    • 终上所述,我们选择了gulp [? ]

    php旧框架现有目录介绍

    └── 项目名
        ├── ....		php文件
        ├── images  静态图片
        ├── include 前端页面静态js、css、images等(gulp运行后主要操作的目录一)
        │	├── ...
        │	├── javascript
        │	├── ...
        │	├── css
        │	├── ...
        │	├── static
        │	├── ...
        │	├── gulpfile.js
        │	├── package.json
        │   └── ...
        ├── lib 		php文件
        ├── ... 		其它php文件
        ├── tmpl 		前端页面模板文件(gulp运行后主要操作的目录二)
        └── ... 		其它配置文件
    

    Gulp打包流程图

    超详细Gulp打包Seajs模块压缩合并,绝对对你 '老' 项目有帮助 ~

    gulp打包依赖

    • gulp:不多讲

    • gulp-uglify:js文件压缩

    • gulp-minify-css:css压缩

    • gulp-fs-cache:文件缓存插件

    • gulp-babel:js文件转成es5语法插件

    • gulp-replace:字符串替换插件

    • pump:小型节点模块,可将stream流连接在一起并在其中一个关闭时将其全部销毁

    • run-sequence:控制多个任务进行顺序执行或者并行执行

    • gulp-591-rev:自定义包 类似:gulp-rev 插件生成的manifest.json文件中的映射

    • gulp-591-collect:自定义包 类似:gulp-rev-collector 替换文件名称, 也可以替换路径

    • gulp-combo-seajs:自定义包 类似:gulp-seajs-combo seajs合并包主要解决现有插件的不能满足的问题

    • gulp-convert-encoding:文件编码转码

    gulp运行

     由于前端资源与模板主要存在于include目录与tmpl目录,故只会对该两个目录进行操作;
    

    1. 操作include目录:

    该文件目录主要保存的是js、css、images静态资源
    

    1.1 复制include目录下的文件

    • 主要是为了区分php的变量访问,防止出现访问错误;
    • on('end', fun) :主要为了区分gulp异步打包机制,防止某些文件复制了被替换的现象;
    // 复制include文件下的某些文件至.build,去做构建处理
    gulp.task('copyInclude', function (cb) {
        gulp.src(copyPaths.paths) // paths为路径处理
            .pipe(gulp.dest('.build'))
            .on('end',function() {
                console.log('copyInclude 执行完成了');
                cb();
            })
    });
    

    1.2 压缩合并js, css文件

    • 压缩css的同时,转义css文件中的相对路径引用,例如:../../../../../

       // 处理并压缩css文件;
       gulp.task('minifyCSS', function () {
           minifyCSS('{css,javascript,static}/**/*.css', '.build')
       })
       
       // ......
       
       /**
        * 壓縮 CSS 公共方法
        * replace(/(..\/){3,5}images\//gi’替换图片路径的正則
        * 匹配 ../../../images/ 字符串內容,../ 是 3~5 個
        * @param {String} path 目標路徑
        * @param {String} destPath 輸出路徑
        */
       function minifyCSS (path, destPath) {
           var cssFsCache = fsCache('.gulpCache/csscache');
           gulp.src(path)
               .pipe(replace(/(..\/){3,5}images\//gi, `//images${env_name}.591.com.tw/`))
               .pipe(cssFsCache)
               .pipe(minifycss({
                   compatibility: 'ie9'
               }))
               .pipe(cssFsCache.restore)
               .pipe(gulp.dest(destPath));
       }
      
    • 压缩js文件同时,做babeles6语法的转义;

       // 处理并压缩 JS 文件
       gulp.task('uglifyJS', ['seaCombo'], function (cb) {
           jsPaths.paths.forEach(function(item,index){
               // index === (jsPaths.paths.length - 1) 解决回调函数次数过多问题,只能回调一次
               uglifyJS(item.entryPath, item.outputPath, index === (jsPaths.paths.length - 1) && cb)
           })
       });
       
       /**
        * JS 壓縮
        * @param {String} path
        * @param {String} destPath
        * @param {回調函數} cb
        */
       function uglifyJS (path, destPath, cb) {
           var jsFsCache = fsCache('.gulpCache/jscache');
           pump([
               gulp.src(path),
               jsFsCache,
               babel({
                   presets: ['@babel/env'],
                   sourceType: 'script' // 簡單理解,不會添加 'use strict'
               }),
               uglify({
                   mangle: { reserved: ['require', 'exports', 'module'] }
               }),
               jsFsCache.restore,
               gulp.dest(destPath)
           ],
           cb)
       }
      
    • 压缩文件时忽略 require, exports, module是为了seajs合并时做处理;

    • seaCombo 任务合并seajs:

       // seajs 的 alias config
       const staticConfig = require('./static/alias/static');
       const houseConfig = require('./static/alias/house');
       const userCenterConfig = require('./static/alias/userCenter');
       const configAlias = Object.assign({}, staticConfig, userCenterConfig, houseConfig);
       
       // 合併 sea.config 中的alias 的 JS
       gulp.task('seaCombo', function (cb) {
       
           const configAliasArr = Object.keys(configAlias);
       
           configAliasArr.forEach(function (item, index) {
       
               // 切割路径有 ? 的时间戳, 为了使seajsCombo插件识别
               let nowItem = configAlias[item].split('?')[0];
       
               // 从相对路径变成绝对路径,为了使seajsCombo插件识别
               configAlias[item] = path.resolve(__dirname, nowItem)
               let srcString = configAlias[item];
               let _base;
       
               const hasStatic = srcString.includes('static');
               const hasJavascript = srcString.includes('javascript');
       
       
               if (hasStatic || hasJavascript) {
                   _base = (hasStatic && 'static') || (hasJavascript && 'javascript')
               } else {
                   return;
               }
               return gulp.src(srcString, { base: _base })
                   .pipe(seajsCombo({
                       map: configAlias,
                       ignore: jsPaths.ignoreConfig
                   }))
                   .pipe(gulp.dest('.build/' + _base))
                   .on('end',function() {
                       index === (configAliasArr.length - 1) && cb();
                   })
           })
       
       });
      
      • 自定义插件gulp-combo-seajs主要解决问题:

        • 支持require("xx.css")引入问题
        • seajs.config的alias别名设置文件名命名模块id,而不是以当前文件名命名模块id;
        • 在部分文件不能识别成正确的.js文件,导致会在 xx.js 文件后面加上 .jsxx.js.js

    1.3 生成js, css文件的hash映射文件

    • 生成hash映射主要为了区分版本戳

      //js生成文件hash编码并生成 rev-manifest.json文件名对照映射
      gulp.task('revJson', function(){
          return gulp.src(['.build/**/*.{js,css}'])
              .pipe(rev())
              .pipe(rev.manifest('rev-manifest.json'))
              .pipe(gulp.dest('.build'));
      });
      

    1.4 更改需要映射的js文件的版本戳(主要针对seajs中的配置)

    • 针对seajs中的配置文件路径添加版本戳

       // 更改需要映射的js文件的版本戳
       gulp.task('revJs', function (cb) {
           jsPaths.seaConfig.forEach(function (item, index) {
               return gulp.src(['.build/rev-manifest.json'].concat(item.entryPath))
                   .pipe(revCollector({
                       replaceReved: true
                   }))
                   .pipe(gulp.dest(item.outputPath))
                   .on('end',function() {
                       if (index === (jsPaths.seaConfig.length - 1) ) {
                           console.log( 'revJs 执行完成了' )
                           cb();
                       };
                   })
           })
       })
      

    1.5 複製.build目录文件到 build 目錄

    // 複製.build目录文件到 build 目錄
    gulp.task('copyBuild', function () {
        return gulp.src('.build/**/*.*')
            .pipe(gulp.dest('build'));
    });
    

    2. 操作tmpl目录:

    • 该文件目录主要保存的是页面模板文件,包括php和html模板

    2.1 复制tmpl模板中的文件 至 tmpl_gulp 文件中

    // 复制tmpl模板中的文件 至 tmpl_gulp 文件中
    gulp.task('copyTmpl', function () {
        // 1、先忽略需要更改的文件模板,以免发生覆盖
        let nowTmpls = ignorePaths(tmplPaths)
        // 2、再进行copy
        return gulp.src(['../tmpl/**/*.*'].concat(nowTmpls))
            .pipe(gulp.dest('../tmpl_gulp'));
    });
    

    2.2 更改tmpl內对应模板文件的CSS,JS版本戳

    gulp.task('revHtml', function () {
        buildSlicePaths(tmplPaths, function (path, destPath) {
            return gulp.src(['.build/rev-manifest.json', path])
                // 1.先把文件直接转成utf-8格式
                .pipe(convertEncoding({
                    from: 'GBK',
                    to: 'utf8'
                }))
                // 2.再去映射修改的文件
                .pipe(revCollector({
                    replaceReved: true
                }))
                // 3.再通过utf-8转换成原来的编码格式
                .pipe(convertEncoding({
                    from: 'utf8',
                    to: 'GBK'
                }))
                // 4.最后生成文件模板
                .pipe(gulp.dest('../tmpl_gulp/' + destPath));
        })
    })
    

    打包后目录

    超详细Gulp打包Seajs模块压缩合并,绝对对你 '老' 项目有帮助 ~

    完整gulpfile.js配置

    /**
    * @description: 舊框架自動構建配置 - 基於 gulp
    * @author:      https://github.com/jeddygong
    * @dateTime:    2021-02-29 14:45:02
    */
    
    // 引入 gulp及组件
    var gulp    = require('gulp'),
        uglify = require('gulp-uglify'),
        fsCache = require('gulp-fs-cache'),
        pump = require('pump'),
        babel = require('gulp-babel'),
        replace = require('gulp-replace'),
        runSequence  = require('run-sequence')
        minifycss = require('gulp-minify-css');
    
    const path = require('path');
    
    const rev = require('gulp-591-rev');
    const revCollector = require('gulp-591-collect');
    const seajsCombo = require('gulp-combo-seajs');
    const convertEncoding = require("gulp-convert-encoding");
    
    // seajs 的 alias config
    const staticConfig = require('./static/alias/static');
    const houseConfig = require('./static/alias/house');
    const userCenterConfig = require('./static/alias/userCenter');
    const configAlias = Object.assign({}, staticConfig, userCenterConfig, houseConfig);
    
    // 當前環境變量
    const env = process.env.NODE_ENV
    
    // 環境變量判斷
    const env_name = (env === 'develop' ? '.debug' : '')
    
    // 需要copy的include目录下的文件
    const copyPaths = {
        paths: [
            '{ajax,bootstrap,css,javascript,static}/**/*.*',
        ]
    }
    
    // 需要映射的 php 或 html 文件中的版本戳 文件,添加到paths中即可
    const tmplPaths = {
        base:  '../tmpl/',
        paths: [
            'index/house/postRent/index.tpl.php'
        ]
    }
    
    // js路径配置
    const jsPaths = {
        // 需要添加合并的seajs文件, 只针对页面是使用include/javascript/public/config.js的配置
        manifestPaths: [
            '.build/javascript/newUserCenter/newMedium/index.js'
        ],
    
        // 基本js文件路径,需要压缩编译的文件
        paths: [
            // javascript 路径下的js文件暂不压缩,要不然会有问题,例子javascript/housing/newList.js
            {
                entryPath: [
                    '.build/javascript/**/*.js',
                    '!.build/javascript/mobile/preloadData.js',
                    '!.build/javascript/house/list.js',
                    '!.build/javascript/seajs/**/*.*',
                    '!.build/javascript/plugin/*.*',
                    '!.build/javascript/newPlugin/*.*'
                ],
                outputPath: '.build/javascript'
            },
            {
                entryPath: ['.build/static/**/*.js'],
                outputPath: '.build/static'
            }
        ],
        // 需要合并添加版本戳替换seajs配置
        seaConfig: [
            {
                entryPath: ['.build/static/alias/static.js', '.build/static/alias/userCenter.js', '.build/static/alias/house.js'],
                outputPath: '.build/static/alias'
            },
            {
                entryPath: ['.build/javascript/public/config.js'],
                outputPath: '.build/javascript/public'
            },
            {
                entryPath: ['.build/javascript/newPublic/dsConfig.js'],
                outputPath: '.build/javascript/newPublic'
            }
        ],
        ignoreConfig: [
            '/include/javascript/jquery.validate.js',
            ...Object.keys(staticConfig)
        ]
    }
    
    // 复制include文件下的某些文件至.build,去做构建处理
    gulp.task('copyInclude', function (cb) {
        gulp.src(copyPaths.paths)
            .pipe(gulp.dest('.build'))
            .on('end',function() {
                console.log('copyInclude 执行完成了');
                cb();
            })
    });
    
    // 处理并压缩css文件;
    gulp.task('minifyCSS', function () {
        minifyCSS('{css,javascript,static}/**/*.css', '.build')
    })
    
    // 处理并压缩 JS 文件
    gulp.task('uglifyJS', ['seaCombo'], function (cb) {
        jsPaths.paths.forEach(function(item,index){
            // index === (jsPaths.paths.length - 1) 解决回调函数次数过多问题,只能回调一次
            uglifyJS(item.entryPath, item.outputPath, index === (jsPaths.paths.length - 1) && cb)
        })
    });
    
    // 合併 sea.config 中的alias 的 JS
    gulp.task('seaCombo', function (cb) {
    
        const configAliasArr = Object.keys(configAlias);
    
        configAliasArr.forEach(function (item, index) {
    
            // 切割路径有 ? 的时间戳
            let nowItem = configAlias[item].split('?')[0];
    
            // 从相对路径变成绝对路径
            configAlias[item] = path.resolve(__dirname, nowItem)
            let srcString = configAlias[item];
            let _base;
    
            const hasStatic = srcString.includes('static');
            const hasJavascript = srcString.includes('javascript');
    
    
            if (hasStatic || hasJavascript) {
                _base = (hasStatic && 'static') || (hasJavascript && 'javascript')
            } else {
                return;
            }
            return gulp.src(srcString, { base: _base })
                .pipe(seajsCombo({
                    map: configAlias,
                    ignore: jsPaths.ignoreConfig
                }))
                .pipe(gulp.dest('.build/' + _base))
                .on('end',function() {
                    // console.log( index === (configAliasArr.length - 1) && 'seaCombo 执行完成了' );
                    index === (configAliasArr.length - 1) && cb();
                })
        })
    
    });
    
    //js生成文件hash编码并生成 rev-manifest.json文件名对照映射
    gulp.task('revJson', function(){
        return gulp.src(['.build/**/*.{js,css}'])
            .pipe(rev())
            .pipe(rev.manifest('rev-manifest.json'))
            .pipe(gulp.dest('.build'));
    });
    
    // 更改需要映射的js文件的版本戳
    gulp.task('revJs', function (cb) {
        jsPaths.seaConfig.forEach(function (item, index) {
            return gulp.src(['.build/rev-manifest.json'].concat(item.entryPath))
                .pipe(revCollector({
                    replaceReved: true
                }))
                .pipe(gulp.dest(item.outputPath))
                .on('end',function() {
                    if (index === (jsPaths.seaConfig.length - 1) ) {
                        console.log( 'revJs 执行完成了' )
                        cb();
                    };
                })
        })
    })
    
    // 複製.build目录文件到 build 目錄
    gulp.task('copyBuild', function () {
        return gulp.src('.build/**/*.*')
            .pipe(gulp.dest('build'));
    });
    
    // 复制tmpl模板中的文件 至 tmpl_gulp 文件中
    gulp.task('copyTmpl', function () {
        // 1、先忽略需要更改的文件模板,以免发生覆盖
        let nowTmpls = ignorePaths(tmplPaths)
        // 2、再进行copy
        return gulp.src(['../tmpl/**/*.*'].concat(nowTmpls))
            .pipe(gulp.dest('../tmpl_gulp'));
    });
    
    // 更改tmpl內对应模板文件的CSS,JS版本戳
    gulp.task('revHtml', function () {
        buildSlicePaths(tmplPaths, function (path, destPath) {
            return gulp.src(['.build/rev-manifest.json', path])
                // 1.先把文件直接转成utf-8格式
                .pipe(convertEncoding({
                    from: 'GBK',
                    to: 'utf8'
                }))
                // 2.再去映射修改的文件
                .pipe(revCollector({
                    replaceReved: true
                }))
                // 3.再通过utf-8转换成原来的编码格式
                .pipe(convertEncoding({
                    from: 'utf8',
                    to: 'GBK'
                }))
                // 4.最后生成文件模板
                .pipe(gulp.dest('../tmpl_gulp/' + destPath));
        })
    })
    
    
    // 默认任务 清空图片、样式、js并重建 运行语句 gulp
    gulp.task('default', ['start']);
    
    gulp.task('start', function () {
        console.log('/********************************', new Date().toLocaleString(), '*******************************************/');
        runSequence(
            ['copyInclude'],
            ['uglifyJS', 'minifyCSS'],
            ['revJson'],
            ['revJs'],
            /**
             * 由于更改了revJs任务 jsPaths.seaConfig文件中的版本戳 并且是在revJson之后修改的
             * 导致更改后的rev-manifest.json没有更新到,所以需要再次生成版本戳
             */
            ['revJson'],
            ['copyBuild'],
            ['copyTmpl'],
            ['revHtml']
        );
    });
    
    /**
     * JS 壓縮
     * @param {String} path
     * @param {String} destPath
     * @param {回調函數} cb
     */
    function uglifyJS (path, destPath, cb) {
        var jsFsCache = fsCache('.gulpCache/jscache');
        pump([
            gulp.src(path),
            jsFsCache,
            babel({
                presets: ['@babel/env'],
                sourceType: 'script' // 簡單理解,不會添加 'use strict'
            }),
            uglify({
                mangle: { reserved: ['require', 'exports', 'module'] }
            }),
            jsFsCache.restore,
            gulp.dest(destPath)
        ],
        cb)
    }
    
    /**
     * 壓縮 CSS 公共方法
     * replace(/(..\/){3,5}images\//gi’替换图片路径的正則
     * 匹配 ../../../images/ 字符串內容,../ 是 3~5 個
     * @param {String} path 目標路徑
     * @param {String} destPath 輸出路徑
     */
    function minifyCSS (path, destPath) {
        var cssFsCache = fsCache('.gulpCache/csscache');
        gulp.src(path)
            .pipe(replace(/(..\/){3,5}images\//gi, `//images${env_name}.591.com.tw/`))
            .pipe(cssFsCache)
            .pipe(minifycss({
                compatibility: 'ie9'
            }))
            .pipe(cssFsCache.restore)
            .pipe(gulp.dest(destPath));
    }
    
    /**
     * @description 需要忽略的文件不被copy,解决gulp的task任务异步问题
     * @param   {Array}     manifestPaths    所有忽略的文件路径
     * @return  {Array}
     */
    function ignorePaths (manifestPaths, base) {
        return manifestPaths.paths.map(function(item,index){
            return '!'+ manifestPaths.base + item;
        })
    }
    
    /**
     * @description 需要打包的文件路径做切割
     * @Example 'javascript/newUserCenter/newMedium/index.js' => 'javascript/newUserCenter/newMedium' 和 'index.js'
     * @param   {Array}     manifestPaths    所有忽略的文件路径
     * @param   {Function}  cb
     */
    function buildSlicePaths (manifestPaths, cb) {
        manifestPaths.paths.forEach(function(item,index){
            let name = item.split('/').length - 1;
            // 查找最后一个/的位置
            let sub = item.lastIndexOf('\/');
            let destPath = '';
            if (sub) {
                destPath = item.slice(0, sub);
            }
            cb(manifestPaths.base + item, destPath, index);
        })
    
    }
    
    

    起源地下载网 » 超详细Gulp打包Seajs模块压缩合并,绝对对你 '老' 项目有帮助 ~

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元