创建seajs minifier的问题记录(一)

首先要说明一下,为什么会有创建seajs minifier的想法。

首先,必须强调的一点是,我非常喜欢seajs的规范及风格,虽然我更早接触的是requirejs,但是我却更喜欢使用的是seajs。

而对于minifier,seajs官方推荐的包管理工具spm3,不可否认的是spm3很强大,但是由于个人性格原因,我却不太喜欢这种过于强大的东东,我更加喜欢一个功能对应一个框架那种简单的方式,意思就是minifier就是minifier,包管理工具就是包管理工具,两者不应该被合并在一起。

于是就有了编写seajs minifier的想法。好在spm3也单独推出了关于grunt-cmd-transport和grunt-cmd-concat的两个插件,所以seajs minifier的大部分思想都是基于这两个插件的基础上做的,也相当于是站在XX的肩膀上的感觉。

这篇博客也仅仅是为了记录一下创作这个的过程,包括一下个人的想法及思路。

好了,进入正题。

首先第一个面临的问题就是:

minifier应该做点什么?

对于我自己而言,minifier应该做的是,指定一个入口module,由minifier抽取出该module所需的依赖,并通过一些配置判定哪些module需要排除出去,最后将该入口module与其相关的需要组合在一起的module合并,并进行minify。

这是minifier需要做的基本功能。

首先第一步就是需要transport seajs module。

原因是seajs的define函数在没有id标识时,会自动根据标识规则为该module生成一个标识,并通过正则表达式找出require语句,从而得到该module所需的依赖。而要引用该module,则又会通过标识规则将引用的path转变成id,然后在cache中查找,找到了就不会再去加载文件,找不到则会根据id得到module的文件地址而动态下载文件。

于是,第一个问题就是,grunt-cmd-transport插件能将seajs module转变成正确的格式么?

建立一个测试工程试试。

目录结构(已包括grunt并添加了grunt-cmd-transport任务)


index.html

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

</body>
<script src="lib/sea-debug.js" id="seajsnode"></script>
<script>
    seajs.use("config",function(config){
        seajs.config(config);
        seajs.use("main",function(main){
            main();
        })
    })
</script>
</html>

config.js

define(function(){
    return {
        base : "./js",
        alias:{
            jquery : "http://code.jquery.com/jquery.min.js"
        },
        paths : {
            "index":"page/index"
        }
    }
})

main.js

define(function(require){
    require("jquery");
    var index = require("index/index");
    return function(){
        $("body").append("<div>main method 启动了</div>");
        index.render();
    }
});

index.js

define(function(require,exports){
    require("jquery");
    exports.render = function(){
        $("body div").append("index page 启动");
    }
});
运行index.html,界面上显示
main method 启动了<span style="font-family: Arial, Helvetica, sans-serif;">index page 启动</span>

OK,seajs测试工程搭建成功。

接着就是尝试使用transport。

添加grunt、grunt-contrib-clean、grunt-cmd-transport。

编写gruntfile.js

module.exports = function(grunt){
    grunt.initConfig({
        transport: {
            js : {
                options:{
                  debug:false
                },
                files:[{
                    expand:true,
                    cwd : "js",
                    src : "**/*.js",
                    dest : "tmp/transport"
                }]
            }
        },
        clean:{
            transports:["tmp"]
        }
    });
    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks('grunt-cmd-transport')
    // By default, lint and run all tests.
    grunt.registerTask('default', ['clean','transport']);

}
得到的结果为:

tmp目录结构


main.js

define("main", [ "jquery", "index/index" ], function(require) {
    require("jquery");
    var index = require("index/index");
    return function() {
        $("body").append("<div>main method 启动了</div>");
        index.render();
    };
});
index.js

define("page/index/index", [ "jquery" ], function(require, exports) {
    require("jquery");
    exports.render = function() {
        $("body div").append("index page 启动");
    };
});
同时,控制台报了3个warning

Warning: can't find module jquery Used --force, continuing.
Warning: can't find module index/index Used --force, continuing.
Warning: can't find module jquery Used --force, continuing.
transport 2 files
由结果可见,配置中的cwd地址指定的文件夹为base地址,在transport过程中,根据这个base地址去生成module标识。

在这次转换过程中,cwd指定地址为js,main在js文件夹下,于是module标识为文件名,index的地址相对于js文件夹为page/index/index,于是module标识为"page/index/index".而在js目录中无法找到“jquery.js”,所以当转换main的时候报了第一个warning,同样的道理,index相对于main的地址为page/index/index,顾通过"index/index"去找也找不到,故报出第二个warning,以此类推第三个warning的原因也就明白了。

手动将index.js的内容加入到main.js中去,并更改config里的base为"./tmp/transport",再次运行index.html.

界面显示

main method 启动了<span style="font-family: Arial, Helvetica, sans-serif;">index page 启动</span>
居然运行成功了,调试了一下,发现,因为不匹配任何一个规则,于是用了该url直接作为module的标识地址,而这个地址正好是文件的正式地址,从而在main module查找依赖时,通过标识匹配转换"index/index"正好与该标识相同,因此可以运行成功。

而一旦我们将guntfile.js中的cwd地址更换一下,这转换后,这index.html就无法运行了。由此我们可以认为transport的cwd地址必须和seajs中定义的config地址保持一致才行,这样就可以保证针对于seajs的base或者是transport的根地址,两个通过标识转换出来的结果是一致的。

阅读transport的源码发现,transport一个module中除了会取出当前module直接依赖的deps,还会去寻找其deps所依赖的deps,最后将所有的deps合并在一起,形成deps数组。关于这个操作,目前还没想明白是否有必要。

既然transport的插件能满足目前所需,那么就开始第二步吧。

第二步就是concat,先说明一下grunt-cmd-concat做的事,指定一个入口module,取出该module的dependencies,然后将其拼接在一个文件中输出。

貌似和我的想法是一致的,但是,concat插件在查找依赖的时候,有3种include的模式,self、relative、all。

self模式是指不拼接依赖。relative模式则会根据形如"./"的相对于该module的文件。all则是全部拼接。

突然想起曾经看seajs文档时发现的一句话,官方推荐的是与module相关的deps尽量采取相对路径的写法,看了这个插件才明白是什么意思。

而实际使用过程中,涉及了很多的具体情况,比如,在config中定义了alias,paths,vars,base,preload,map等等配置,以至于如果仅仅分析相对路径的写法又显得简单了点。仔细查看文档,发现了concat的配置中有paths的设置,但是并没有写得很详细。

查看源码后发现

  options.paths.some(function(base) {
      var filepath = path.join(base, file);
      if (grunt.file.exists(filepath)) {
        grunt.log.verbose.writeln('find module "' + filepath + '"');
        fpath = filepath;
        return true;
      }
    });
paths的配置仅仅用在了这样一个地方。不难发现上面的意思是通过定义paths去帮助module查找到dependency,但是是采用遍历的后判定文件是否存在的方式。

也就是说,如果你定义了2个paths,且这两个paths里都有依赖的文件,则会一起拼接到该文件中。

老实说,这个paths的意义并不是很大。

这样,难道就没有办法实现一个完美的拼接方案了么?

现阶段有了下面的几个想法

1、通过transport之后的module,按照我们之前的测试,发现其dependencies里的路径并没有发生改变。该路径可能是相对路径,也可能是绝对路径,也可能是通过alias,paths等seajs的配置定义出来的路径。

2、在seajs的配置中,vars是属于一个变化的参数,有使用vars的路径往往都是变化的路径,而我们在做minifier的时候,变化的路径并不会拼接在一起,比如locale,会根据当前的应用情况而选择不同的locale文件,这个时候把所有的locale文件并在一起也是不符合常规的。这种情况我们就不考虑了。

3、在seajs中map的配置情况比较复杂,但这只是加载文件的一种变化,如果把这种变化拼接进minfier中,也不太符合常规,这种情况也不考虑了。

4、在seajs中还有preload的配置。虽然2.0版本后,已经讲preload做成了插件移出了主代码中。但是这个配置依然需要考虑。preload的意义在意use任何module之前先加载一些js,或者module。虽然在页面有多个use的情况下preload是不太好拼接进module中,但在单个module的情况下,preload还是有可能拼接进module中的。

所以,以上所述表明,我们要做拼接需要考虑到配置中的alias,paths,base和preload。且preload是否需要拼接进module也要可以选择。

如果,我们把seajs config里的这些配置移到concat上去帮忙查找dependency,是否可行呢?


先细想一下流程。

首先,transport所有的js文件,其次,通过定义一个入口module,查找出该module的dependencies,要先满足preload 的module可以选择性的拼接进输出文件中,那么此时就需要一个配置,配置的类型这如同preload:[]的形式,当该数组为空的时候,我们就跳过这一步。然后通过alias,paths,base等配置去解析dependencies数组中的路径,将其还原成文件的真实位置,最后找到文件,读取内容拼接进module中输出。

看似流程的关键在于通过alias,paths,base等参数将dependencies中的路径还原这一步最为关键。不过这一步,完全可以参照seajs解析路径的方式去解析。因为seajs在生成module标识的时候,采用的就是这种解析方式。

那么,就上手试试

终于理解了“transport一个module中除了会取出当前module直接依赖的deps,还会去寻找其deps所依赖的deps,最后将所有的deps合并在一起,形成deps数组。”

有了这一步,在concat的过程中就不想要去递归的查找dependencies的依赖而进行拼接。只是,为什么要把这一步放在transport过程中呢?有点不太理解。

文章太长了。。新开一篇继续吧。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
**数字乡村解决方案摘要** **国家战略与乡村振兴** 中国正实施国家大数据战略,以加快数字中国的建设,整合数据资源,保障数据安全,并推动产业升级。2023年中央一号文件强调了乡村振兴战略,旨在通过产业兴旺、生态宜居、乡风文明、治理有效和生活富裕五大方面,实现农业农村现代化。 **数字乡村建设的重要性** 数字乡村建设是乡村振兴战略的重要组成部分,通过整合资源数据,推动物联网和遥感技术在农业中的应用,促进农业现代化。它被视为促进乡村转型发展的新方向,是数字化在农业农村经济社会发展中的应用。 **关键要素与效益** 数字乡村建设的关键要素包括数据资源整合、产业促进、农业发展规模化和标准化,以及改善乡村生活服务体系。预期效益包括提升国家治理能力,实现政府决策科学化和社会治理精准化,同时推动公共服务高效化。 **体系架构与数据融合** 数字乡村的体系架构包括乡村生态、基层治理、慧治、慧享、慧融、慧美、慧智和慧图等多个方面,实现城乡、政企、农户各级平台及服务的融合。数据融合是核心,整合乡村人口、产值、环境监测等多方面数据,为乡村治理和产业发展提供支撑。 **多业务协同与智慧治理** 数字乡村通过多业务协同应用平台,实现乡村物联网的布局和触达,涵盖农业生产、农资厂商、消费者等环节。区块链技术在农产品质量溯源中的应用,确保了产品全过程可追溯。乡村智慧治理通过村务管理、财务管理等方面提升治理效能,实现绿色发展理念。综合服务体系通过“互联网+服务”模式,提供便民服务和乡村经济发展支持,推动乡村全面振兴。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值