之前的项目一直采用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:

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

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

但是在前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的模块进行组织的一种文件夹结构,它是这样的:

这个结构借鉴于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下的文件的时候,对这些文件的加载地址做一点更新:

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

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

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

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

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

这个是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合并之后是这个样子:

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

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

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

2. 本文小结

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

介绍一种基于gulp对seajs的模块做合并压缩的方式的更多相关文章

  1. 一种基于Qt的可伸缩的全异步C/S架构服务器实现(流浪小狗,六篇,附下载地址)

    本文向大家介绍一种基于Qt的伸缩TCP服务实现.该实现针对C/S客户端-服务集群应用需求而搭建.连接监听.数据传输.数据处理均在独立的线程池中进行,根据特定任务不同,可安排负责监听.传输.处理的线程数 ...

  2. 一种基于Qt的可伸缩的全异步C/S架构server实现(一) 综述

    本文向大家介绍一种基于Qt的伸缩TCP服务实现.该实现针对C/Sclient-服务集群应用需求而搭建. 连接监听.传输数据.数据处理均在独立的线程池中进行,依据特定任务不同,可安排负责监听.传输.处理 ...

  3. 一种基于匹配回朔的 css3 选择器引擎实现

    介绍 CSS 选择器是一种应用于 DOM 节点查找场景的特定微型语法, 本质上和正则表达式一样都是一种模式匹配语言,灵活使用可以方便得获取指定位置的节点集合. 目前 W3C 推荐标准为 Selecto ...

  4. 一种基于Qt的可伸缩的全异步C/S架构服务器实现(一) 综述

    本文向大家介绍一种基于Qt的伸缩TCP服务实现.该实现针对C/S客户端-服务集群应用需求而搭建.连接监听.数据传输.数据处理均在独立的线程池中进行,根据特定任务不同,可安排负责监听.传输.处理的线程数 ...

  5. 关于用gulp合并压缩seaJs模块

    现在很多人都在用seaJs来开发项目,seaJs上手容易,操作简单.但在后期做合并压缩的时候却中了个巨大无比的坑,但坑也总得有人来填.于是花了将近一个星期的时间来填了这坑,现将填坑的一些心得与大家分享 ...

  6. 极其简单的使用基于gulp和sass前端工作流

    简单的记录自己如何在实际工作中使用gulp和sass的.我的原则是,小而美! gulp与sass介绍 gulp 什么是gulp?和Grunt一样,是一种任务管理工具:和Grunt又不一样,gulp是一 ...

  7. 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

    通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...

  8. 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一)

    相关连接导航 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二) 常用 Gulp 插件汇总 ...

  9. [转]基于gulp和webpack的前端工程化

    本文样例代码 :https://github.com/demohi/learning-gulp 本文主要简单介绍一下基于gulp和webpack的前端工程化. 技术栈 React.js reFlux ...

随机推荐

  1. "SQL Server does not handle comparison of NText, Text, Xml, or Image data types."

    "SQL Server does not handle comparison of NText, Text, Xml, or Image data types." sql2000 ...

  2. JQM (功能栏、导航条)

    在Mobile中导航条的基本结构: <div data-role="navbar"> ul>li>a </div> 其中含有“行(grid)”和 ...

  3. 有了lisk,为什么我们还要做一个Asch?

    0 前言 首先要声明一点,我们和我们的一些朋友都是lisk的投资人和支持者,我们也相信lisk会成功. 事实上,lisk已经成功了一半,目前在区块链领域融资金额排行第二,仅次于以太坊. 那为什么我们还 ...

  4. 使用 SoapUI 测试ASP.NET Web API

    我们为不同的目的开发了很多web服务,经过授权的用户就可以访问和使用这些web服务.soapUI 是一个强大的测试web服务的工具,他不仅可以测试SOAP服务,他也支持测试RESTful服务.在这里我 ...

  5. ABP理论学习之Web API控制器(新增)

    返回总目录 本篇目录 介绍 AbpApiController基类 本地化 审计日志 授权 工作单元 其他 介绍 ABP通过Abp.Web.ApiNuget包集成了 ASP.NET Web API控制器 ...

  6. .NET中那些所谓的新语法之二:匿名类、匿名方法与扩展方法

    开篇:在上一篇中,我们了解了自动属性.隐式类型.自动初始化器等所谓的新语法,这一篇我们继续征程,看看匿名类.匿名方法以及常用的扩展方法.虽然,都是很常见的东西,但是未必我们都明白其中蕴含的奥妙.所以, ...

  7. 如果调用ASP.NET Web API不能发送PUT/DELETE请求怎么办?

    理想的RESTful Web API采用面向资源的架构,并使用请求的HTTP方法表示针对目标资源的操作类型.但是理想和现实是有距离的,虽然HTTP协议提供了一系列原生的HTTP方法,但是在具体的网络环 ...

  8. UWP自动填充控件AutoSuggestBox小优化

    UWP提供的AutoSuggestBox本身非常好用,在项目中经常用到,但是当我们使用时发现一下不人性化的设置,例子1如下: <Page x:Class="SelfInkCanvas. ...

  9. Android开发学习之路-RecyclerView使用初探

    在进行一些MaterialDesign规范开发的时候,比如之前说到的CoordinateLayout实现的向上折叠效果的时候,如果依然使用ListView,那么这种效果是做不出来的,因为ListVie ...

  10. iOS-多线程基础

    进程与线程: 1>   一个应用程序对应一个进程,一个进程帮助程序占据一块存储空间 2>   要想在进程中执行任务,就必须开启线程,一条线程就代表一个任务 3>   一个进程中允许开 ...