前端工程化二(requirejs + gulp)

利用gulp+requirejs解决了目前项目的两个主要问题。css

静态资源缓存问题

requirejs对于js文件的版本号是经过config.js配置文件中的urlArgs参数统一管理的。html

requirejs.config({
        ...
        urlArgs:"v=1.0.0"
        path:{
            "module1":"./module1",
            "module2":"./module2"
        }
        ...
})

requirejs会在装载module1和module2时,在请求后面拼接上"?v=1.0.0"。
问题在于,每次发布,无论哪一个环境,都须要手动去修改urlArgs这个参数,不然就会出现缓存问题。
而但愿实现的目标是,全部静态资源根据MD5码生成版本号。
解决思路:
一种是利用gulp将配置文件调整成如下样子:json

requirejs.config({
        ...
        urlArgs:""
        path:{
            "module1":"./module1.js?v=dFe82Pzk",
            "module2":"./module2.js?v=1a0Ak9pY"
        }
        ...
})

大体实现流程:
1.读取配置文件,将文件中path解析成JSON对象,前提条件是配置文件中path的键值都是string且都用引号引发来,不然没法转换成json对象;gulp

//获取requireCOnfig中的paths JSON对象
const __getRequireConfigPaths = function (file) {
    let configContents = file.contents.toString();
    let matches = /"paths": (\{[^}]*}),/.exec(configContents);
    return JSON.parse(matches[1]);
};

2.遍历全部require管理的js文件进行MD5编码,创建moduleName:md5键值对构成的对象verMap;promise

//根据文件内容计算md5串
const __getDataMd5 = function (data) {
    return crypto.createHash('md5').update(data).digest('base64');
};

//根据paths JSON对象生成[{moduleName:moduleNamev?=MD5}]对照关系verMap
gulp.task("createMD5VerMap", function (cb) {
    gulp.src("./config/common-config.js")
        .pipe(through2.obj(function (file, encoding, done) {
                let paths = __getRequireConfigPaths(file);
                //将through2异步操做封装成promise对象,利用Promise.all等待全部through2异步任务执行完毕后调用gulp.task的callback
                let promises = [];
                for (let moduleName in paths) {
                    let filePath = './' + paths[moduleName];
                    let suffix = '';
                    if(/([Cc]ss$)/.test(moduleName)){
                        //需约定moduleName以css或者Css结尾对应的都是css文件
                        filePath = './' + paths[moduleName]+'.css';
                        suffix = '.css'
                    }else if(!/(\.html$)/.test(filePath)){
                        //需约定文件名不以.html结尾的都是js文件
                        filePath = './' + paths[moduleName]+'.js';
                        suffix = '.js'
                    }
                    promises.push(
                        new Promise(function (resolve) {
                            gulp.src(filePath)
                                .pipe(through2.obj(function (file) {
                                        verMap[moduleName] = paths[moduleName]+suffix+"?v="+__getDataMd5(file.contents).slice(0, 6);
                                        resolve();
                                    }, function () {
                                        //不处理失败任务
                                        console.log("未获取到文件:"+paths[moduleName])
                                        verMap[moduleName] = paths[moduleName];
                                        resolve();
                                    })
                                )
                        })
                    );
                }
                Promise.all(promises).then(cb());
            })
        );
});
这里利用Promise.all来保证所有文件完成MD5编码后再进行后续处理,防止异步问题致使verMap未完整生成就被拿去作其余处理。

3.读取配置文件,根据verMap改写path中的值("module1":"./module1" => "module1":"./module1?v=dFe82Pzk")缓存

//将[{moduleName:MD5}]对照关系verMap写入requireConfig.js配置文件中
gulp.task("modifyRequireConfig", function () {
    return gulp.src("js/requirejs-config.js")
        .pipe(through2.obj(function (file, encoding, done) {
            let contents = file.contents.toString();
            contents = contents.replace(/"paths": (\{[^}]*}),/, '"paths": '+JSON.stringify(verMap));
            file.contents = new Buffer(contents);
            this.push(file);
            done();
        }))
        .pipe(rename("requirejs-config.js"))
        .pipe(gulp.dest("./js"));
});

这种方法的弊端在于静态资源的覆盖率,对于未在path中配置的静态文件,是没办法打上md5版本号的,因此须要对全部用require或者define引入的js文件,所有写到path里去,致使配置文件臃肿。app

网上还有种思路是修改require.js源码,将urlArgs改成容许传入Function,在根据全部静态文件生成moduleName:MD5键值对之后,经过动态获取MD5码来拼接url。(参考:http://www.tuicool.com/articl...)异步

若是对修改源码没有限制,能够采用这种方式。requirejs

代码合并、压缩

利用requirejs的optimize方法,能够将存在相互依赖关系的几个js合并成单个js文件,并提供uglify压缩。ui

module1:
    define("module1", function(){
        return {
            key: "value"
        }
    });

module2:
    require(["module1"], function(module1){
        console.log(module1.key);
    })
    
gulpfile.js
    gulp.task("concat", function(){
        rjs.optimize({
            baseUrl: "./",
            name: "./test/module2.js",
            out: "./test/app.js",
            optimize: "uglify"//压缩
        }, function () {
            console.log(name + ":" + out + " is OK!");
        });
    });

执行gulp任务后会将module1和module2合并生成app.js。

app.js:
    define("module1",[],function(){return{key:"value"}}),define("test/module1.js",function(){}),require(["./module1.js"],function(e){console.log(e.key)}),define("test/module2.js",function(){});

而对于没有相互依赖关系的js,能够用gulp-concat进行合并,再用gulp-uglify压缩

//公共模块合并
gulp.task('buildCommModule', function () {
    return gulp.src(["./js/a.js", "./js/b.js"])
        .pipe(concat("common.js"))
        .pipe(uglify())
        .pipe(gulp.dest('./dist/js/'))
})

最后,对于入口html文件,引入js的代码以下

<script src="../js/requirejs/require.min.js"></script>
<script>
    //禁用requirejs-config.js缓存
    require.s.contexts._.config.urlArgs = new Date().getTime().toString();
    
    require(['../js/requirejs-config'], function () {
        require(['task_main']);
    });
</script>
相关文章
相关标签/搜索