基于gulp合并压缩Seajs模块的方式说明


Posted in Javascript onJune 14, 2016

之前的项目一直采用grunt来构建,然后用requirejs做模块化,requirejs官方有提供grunt的插件来做压缩合并。现在的项目切到了gulp,模块化用起了seajs,自然而然地也想到了模块合并压缩的问题。

然后一开始在解决这个问题的时候,并不是很顺利,在npm上并没有那种特别流行的专门用来做seajs合并压缩的gulp插件,虽然在seajs的github上也看了不少的issue,但是大多数都是只能将所有的模块文件合并成一个总的文件,这对于单页面的应用来说肯定没有问题,但是对于多页面的应用而言,显然就违背了模块化思想中按需加载的核心,所以我想要的是一个能够根据我每个页面各自所依赖的模块来按需合并的方法。

这个按需合并的意思,一方面是只合并一个页面所依赖的那些模块,另一方面是,还能过滤掉某些模块不参与合并,考虑这个的原因在于有些模块,比如jquery等,都属于第三方依赖的库,可能文件比较大,最重要的是你几乎不会去改动它的代码,所以这些模块不合并到页面的js中,会有助于更好地利用浏览器缓存。本文介绍一个简单可行的办法,来做基于gulp构建的中小型项目中的seajs合并压缩。

注:为了说明,seajs合并后的效果,本文提供了一个演示demo,它有两个页面:login.html和regist.html,分别用来模拟多页应用中的两个独立的文件,可通过以下链接来查看:

http://liuyunzhuge.github.io/blog/seajs/dist/html/login.html

http://liuyunzhuge.github.io/blog/seajs/dist/html/regist.html

以login.html为例,查看这个页面的源文件中,会看到它除了引用seajs以及相关的配置文件common.js外,只引用了app/login作为页面的main js,这个app/login模块其实对应的就是js/app/login.js:

基于gulp合并压缩Seajs模块的方式说明

但实际上,这个login.js依赖了更多的模块js,你可以通过chrome的soures来查看该页面加载的详细js资源:

基于gulp合并压缩Seajs模块的方式说明

基于gulp合并压缩Seajs模块的方式说明

在login.js合并之前,它的代码是这样的:

基于gulp合并压缩Seajs模块的方式说明

但是在前2个截图中,我们并没有看到mod/mod1.js , mod/mod2.js , deps/fastclick.js这三个文件,我们除了看到login.js,还看到了lib/bootstrap.js  , lib/jquery.js , lib/jquery.validate.js。这就是合并的效果。一方面保持了js/lib文件夹下的模块都不会参与合并,另一方面保证了页面的main js所依赖的其它模块,都合并到页面的main js文件中来。

该demo相关的代码可通过以下链接进行查看:

https://github.com/liuyunzhuge/blog/tree/master/seajs

1. 合并思路

其实方法还算比较简单,我最后再介绍。

1)我先说下自己对seajs的模块进行组织的一种文件夹结构,它是这样的:

基于gulp合并压缩Seajs模块的方式说明

这个结构借鉴于requirejs,尽量让文件组织扁平化,对于中小型前端项目来说,应该不会太麻烦。其中各个文件夹的作用是:

1)js/app 存放各个页面的main js,基本上是一个页面一个js的逻辑

2)js/deps 存放哪些需要被合并到main js的第三方模块

3)js/lib 存放哪些不需要参与合并的第三方模块

4)js/mod 存放各个项目中自己写的一些js模块

5)common.js 是seajs的配置文件。

2)在common.js中把js/lib下的模块都配置到了alias选项里面,因为这些js都不参与合并,需要使用到浏览器缓存,alias可以方便我们在修改或升级了js/lib下的文件的时候,对这些文件的加载地址做一点更新:

基于gulp合并压缩Seajs模块的方式说明

base配置到了js文件夹。在模块开发中,要require其他模块时,我的习惯都是直接写mod/mod1这样的模块标识,不用相对标识,即使要定义的这个模块所依赖的模块跟它存在于同一个文件夹中,这也是我为啥把base目录设置到js文件夹的原因,有点类似站点根目录的感觉。

3)合并的思路:主要是利用gulp-seajs-transport和gulp-seajs-concat这两个gulp插件。虽然它们在github上不是很热门,但是已经很好地解决我的问题了,使用起来也非常简单:

基于gulp合并压缩Seajs模块的方式说明

(更多内容需要查看本文开始处提供的源码链接,找到相关的gulpfile.js文件)

gulp-seajs-transport可以帮助你把seajs的模块文件从匿名模块,变成具名模块。比如js/mod/mod1.js在构建前是这样的:

基于gulp合并压缩Seajs模块的方式说明

但是经过transport处理后就会变成:

基于gulp合并压缩Seajs模块的方式说明

这个是seajs合并工作中比较关键的一点,它不像requirejs,直接做concat即可;它必须先经过一个transport的任务处理,将匿名模块变成具名模块,同时用define的第二个参数来描述这个模块的所有依赖,就像requirejs那样。只有做完了transport,才能利用gulp-seajs-concat做合并。原因请参考:https://github.com/seajs/seajs/issues/426。

gulp-seajs-concat做合并的时候,就很简单了,只要告诉它一个base选项即可,这个base选项跟js/common.js中base选项保持一致。因为gulp-seajs-concat根据base和transport之后的模块,就能找到它所依赖的其它模块文件。

4)页面中使用main js时要采用这种方式:

use的参数名称,必须跟合并之后的main js的主模块ID保持一致。比如js/app/login.js合并之后是这个样子:

基于gulp合并压缩Seajs模块的方式说明

第一个define对应的模块就是合并后文件内的主模块,红框的内容就是该主模块的id,seajs use这个模块的时候,参数名称必须和这个id一致。否则seajs即使成功的加载到了这个文件,也不会执行任何模块内的代码。因为seajs有一个规则:ID 和路径匹配原则,其中有点跟这个相关,就是:当seajs use到一个文件内包含多个模块时,会根据use的参数名来寻找这个文件内的主模块,只有它们完全匹配,才能找得到。

5)压缩混淆:使用gulp-uglify:

基于gulp合并压缩Seajs模块的方式说明

但是要注意那个mangle,必须把require exports module排除掉,否则会引发一些意外的问题。

2. 本文小结

本文内容虽然很简单,但是在刚切到gulp和seajs的时候,还是费了不少时间才把本文的问题解决,虽然在准备demo的时候进展地比我当时的情况要顺利的多…不管怎么样,希望本文的内容多多少少能帮助到一些朋友。

Javascript 相关文章推荐
JavaScript子类用Object.getPrototypeOf去调用父类方法解析
Dec 05 Javascript
原生JS版和jquery版实现checkbox的全选/全不选/点选/行内点选(Mr.Think)
Oct 29 Javascript
node+express制作爬虫教程
Nov 11 Javascript
清除js缓存的多种方法总结
Dec 09 Javascript
JS 循环li添加点击事件 (闭包的应用)
Dec 10 Javascript
javascript 网页进度条简单实例
Feb 22 Javascript
jquery+css3实现熊猫tv导航代码分享
Feb 12 jQuery
JS在if中的强制类型转换方式
Jul 15 Javascript
给localStorage设置一个过期时间的方法分享
Nov 06 Javascript
微信小程序分享功能onShareAppMessage(options)用法分析
Apr 24 Javascript
解决vue中使用proxy配置不同端口和ip接口问题
Aug 14 Javascript
p5.js绘制旋转的正方形
Oct 23 Javascript
JS去除空格和换行的正则表达式(推荐)
Jun 14 #Javascript
javascript用正则表达式过滤空格的实现代码
Jun 14 #Javascript
三种带箭头提示框总结实例
Jun 14 #Javascript
js判断输入字符串是否为空、空格、null的方法总结
Jun 14 #Javascript
简单实现的JQuery文本框水印插件
Jun 14 #Javascript
JS不用正则验证输入的字符串是否为空(包含空格)的实现代码
Jun 14 #Javascript
浅析jQuery 3.0中的Data
Jun 14 #Javascript
You might like
德生S2000电路分析
2021/03/02 无线电
一个简单的MySQL数据浏览器
2006/10/09 PHP
php打开文件fopen函数的使用说明
2013/07/05 PHP
一组PHP加密解密函数分享
2014/06/05 PHP
Joomla调用系统自带编辑器的实现方法
2016/05/05 PHP
php打开本地exe程序,js打开本地exe应用程序,并传递相关参数方法
2018/02/06 PHP
CL vs ForZe BO5 第三场 2.13
2021/03/10 DOTA
由prototype_1.3.1进入javascript殿堂-类的初探
2006/11/06 Javascript
理解Javascript_05_原型继承原理
2010/10/13 Javascript
jQuery为iframe的body添加click事件的实现代码
2011/04/07 Javascript
jquery实现拖拽调整Div大小
2015/01/30 Javascript
jQuery实现鼠标滑过链接控制图片的滑动展开与隐藏效果
2015/10/28 Javascript
window.onload绑定多个事件的两种解决方案
2016/05/15 Javascript
js返回顶部实例分享
2016/12/21 Javascript
JS正则替换去空格的方法
2017/03/24 Javascript
详解原生js实现offset方法
2017/06/15 Javascript
微信小程序实现根据字母选择城市功能
2017/08/16 Javascript
基于js中的原型(全面讲解)
2017/09/19 Javascript
ES6中Array.includes()函数的用法
2017/09/20 Javascript
浅谈vue项目4rs vue-router上线后history模式遇到的坑
2018/09/27 Javascript
vue.js 实现a标签href里添加参数
2019/11/12 Javascript
Javascript地址引用代码实例解析
2020/02/25 Javascript
Python安装第三方库的3种方法
2015/06/21 Python
Python模块包中__init__.py文件功能分析
2016/06/14 Python
Python中断言Assertion的一些改进方案
2016/10/27 Python
python基础教程项目三之万能的XML
2018/04/02 Python
Python图像处理实现两幅图像合成一幅图像的方法【测试可用】
2019/01/04 Python
通过PYTHON来实现图像分割详解
2019/06/26 Python
利用keras加载训练好的.H5文件,并实现预测图片
2020/01/24 Python
CSS3 transition 实现通知消息轮播条
2020/10/14 HTML / CSS
考试不及格的检讨书
2014/01/22 职场文书
难忘的一天教学反思
2014/04/30 职场文书
党的群众路线专项整治方案
2014/11/03 职场文书
警告通知
2015/04/25 职场文书
2015企业年终工作总结范文
2015/05/27 职场文书
pd.drop_duplicates删除重复行的方法实现
2022/06/16 Python