seajs, spm + grunt 自动模块化前端代码记录

Seajs是玉伯写的一个JavaScript模块化管理加载器,本人最近学习了一下Seajs的使用方法并总结了一套个人的使用规范也算是记录一下使用过程吧。

最重要的一点,seajs的路径加载规则

seajs的模块查找规则,是个非常值得记录的话题,seajs的创作团队自己也说seajs的模块加载是个万人坑,很容易糊涂。在这里,我不对 require('xxxx.js') 这种以.js后缀或者一些其他反人类的使用方式进行特别研究,我只记录出来我所使用到的技术点,当然这些技术点足以满足大多数需求。

很重要的seajs.config

  • base - 指出seajs的模块加载基准地址(有什么作用,下文会说),注意:默认的base是sea.js所在的路径
  • alias - 给模块定义别名,注意:这个配置非常重要,他重要到什么地步呢?不同团队编写的不同模块,放到不同位置,但是,也要相互依赖,就靠这个配置了(怎么用?下文会说)

在代码中以 require(' ./modulename ') 形式加载模块

对于这个情况,seajs会在当前目录下进行寻找模块,比如以下一个文件结构如下:

- core/
     - main.js
     - foo.js

我在 main.js 中以 require('./foo') 的形式可以引用到foo,当看到 ./ 时,就认为是在本目录查找。

在代码中以 require('modulename') 形式加载模块

针对这个情况,seajs会在config.base定义的目录下进行模块查找,config.base的默认路径是sea.js所在的目录(上文已说),还是拿这个目录结构举例子。

- base(这是基准目录)
  - core/
     - main.js
     - foo.js
  - user/
     - login.js

假如我在 core/main.js 中 requlre('user/login'),就会去加载login这个模块。另外我们有时候会有需求去加载jquery,但是jquery目录非常深,它放在了基准目录base下,路径为 base/lib/jquery/1.10.2/jquery.js,那这个时候我们每次编码的时候都去这样写会很累,此时遇到这种需求,alias的好处就出现了,我们可以通过seajs.config设置 alias

seajs.config({
     base : 'base',
     alias : {
          'jquery' : 'lib/jquery/1.10.2/jquery.js'
     }
});

然后,我们可以在代码中直接 require('jquery')来使用jquery模块。对于alias的这个特性,我认为是很有用的,一个团队,往往会负责不同的模块,编写完模块后,发布的地址也不一样,这个时候alias就派上用场了,团队与团队直接不需要知道代码细节,只需要知道将要使用的模块名和模块api就可以进行协同开发了,这样的好处是,开发core的开发core,开发业务的开发业务,互不影响,互不干扰,这样也提升了工作效率,降低了代码管理成本,是一件很好的事。

使用seajs来管理开发版代码

在一开始动工时,我参考了支付宝团队的aralejs前端组件库的组成结构(也就是文件夹结构),其实上文也说了,通过模块化之后可以让不同的开发小组各司其职,各自负责各自的模块,而相互关联的模块直接通过alias别名配置来完成相互引用,这是非常有意义的事。那么开发阶段的代码结构是怎样的呢,在这里不得不提一个关键概念:seajs标准模块结构。请看下面的结构:

- core
     - src/
          - main.js
          - number.js
          - user/
               login.js
     - package.json

这,就是一个标准seajs模块结构,在一个模块中,src下放的是源代码,package.json记录着模块的名称、版本、所属的组等信息。为什么要用标准seajs结构,因为seajs配套的spm打包是很方便的,而且我相信,使用配套的工具的出错率肯定会有所降低。

在我的前端代码中会有很多类似core结构的代码包,每一个代码包代表一个独立的模块,模块我认为可以是线上最小单位了,也就是说上线之后模块与模块将不会再进行js合并。

使用spm + grunt来构建基于seajs编写的js

这部分内容写的有点儿难懂,我声明几个概念吧:

  • 包:表示一个具体的带有 package.json 和 src 源码包的文件夹,比如上面的那个 core 就是一个包
  • 模块:表示包的src下面一个一个的 js 文件

OK,开发时的代码结构划分好之后,剩下的工作就是构建上线了。对于seajs的构建,本人强烈建议使用spm工具,当然网上也有纯grunt版本的实现,不过我试过,很麻烦的样子,反正我没搞通。在这里我介绍一下我自己的方法,grunt + spm。中心思想很简单,使用grunt来跑命令行,执行spm打包流程,然后根据每一个包的package.json信息创建带有版本号的包目录,同时将spm构建后的代码复制进去。spm构建完成后会在每一个包根目录(注意是包的根目录)生成一个dist文件夹,在这个文件夹中保存着合并后的代码,与此同时,我会使用grunt去读每一个包的package.json文件,然后会在一个指定的目录下按package.json中的信息生成以下目录结构:

包在构建前的状态是这样的

- core
     - src/
          - _main.js
          - number.js
          - user/
               login.js
     - package.json

package.json 中记录的信息:

{
     "family" : "project",
     "name" : "core",
     "version" : "0.1.0",
     "description" : "core和新模块包",
     "spm" : {
          "output": ["_main.js"]
     }
}

PS: 这是我自己的个人习惯,每一个包都声明一个 _main.js 模块。

总结

本文总结的很片面,对于CMD的模块定义规范,seajs的众多有用的插件都没有进行特别的介绍,理解本文罗列的要点也许需要你对seajs有一定的了解。