基于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技巧之不要用for in语句对数组进行遍历
Oct 20 Javascript
关于onScroll事件在IE6下每次滚动触发三次bug说明
Sep 21 Javascript
js bind 函数 使用闭包保存执行上下文
Dec 26 Javascript
javascript unicode与GBK2312(中文)编码转换方法
Nov 14 Javascript
JavaScript中变量声明有var和没var的区别示例介绍
Sep 15 Javascript
Node.js实现JS文件合并小工具
Feb 02 Javascript
javascript this详细介绍
Sep 19 Javascript
Node.js中 __dirname 的使用介绍
Jun 19 Javascript
JavaScript数组的5种迭代方法
Sep 29 Javascript
vue-router传递参数的几种方式实例详解
Nov 13 Javascript
vue 表单之通过v-model绑定单选按钮radio
May 13 Javascript
JS addEventListener()和attachEvent()方法实现注册事件
Jan 11 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
php模拟js函数unescape的函数代码
2012/10/20 PHP
PHP mail()函数使用及配置方法
2014/01/14 PHP
WordPress中"无法将上传的文件移动至"错误的解决方法
2015/07/01 PHP
php抓取并保存网站图片的实现代码
2015/10/28 PHP
php打包网站并在线压缩为zip
2016/02/13 PHP
Add a Formatted Table to a Word Document
2007/06/15 Javascript
JavaScript高级程序设计(第3版)学习笔记11 内建js对象
2012/10/11 Javascript
javascript中parentNode,childNodes,children的应用详解
2013/12/17 Javascript
Jquery基础教程之DOM操作
2015/08/19 Javascript
jquery编写Tab选项卡滚动导航切换特效
2020/07/17 Javascript
js清除浏览器缓存的几种方法
2017/03/15 Javascript
微信小程序 监听手势滑动切换页面实例详解
2017/06/15 Javascript
JavaScript实现三级联动效果
2017/07/15 Javascript
js设置随机切换背景图片的简单实例
2017/11/12 Javascript
JS使用正则表达式实现常用的表单验证功能分析
2020/04/30 Javascript
[01:00]DOTA2 store: Collection of Artisan's Wonders
2015/08/12 DOTA
[40:10]2015国际邀请赛全明星表演赛
2015/08/07 DOTA
Python基于pillow判断图片完整性的方法
2016/09/18 Python
TensorFlow实现非线性支持向量机的实现方法
2018/04/28 Python
Python设计模式之代理模式实例详解
2019/01/19 Python
python opencv捕获摄像头并显示内容的实现
2019/07/11 Python
Django获取应用下的所有models的例子
2019/08/30 Python
关于Python turtle库使用时坐标的确定方法
2020/03/19 Python
Keds加拿大官网:购买帆布运动鞋和皮鞋
2019/09/26 全球购物
美国体育用品商店:Academy Sports + Outdoors
2020/01/04 全球购物
NET程序员上机面试题
2015/05/23 面试题
介绍一下.NET构架下remoting和webservice
2014/05/08 面试题
客服主管岗位职责
2013/12/13 职场文书
暑期研修感言
2014/02/17 职场文书
互联网电子商务专业毕业生求职信
2014/03/18 职场文书
银行贷款承诺书
2014/03/29 职场文书
2015年房产销售工作总结范文
2015/05/22 职场文书
2015入党自传格式范文
2015/06/26 职场文书
创业计划书之网络外卖
2019/10/31 职场文书
Redis如何实现验证码发送 以及限制每日发送次数
2022/04/18 Redis
一文了解Java动态代理的原理及实现
2022/07/07 Java/Android