首页 关于我们

浅析模块化和seajs

Word count: 1.4k / Reading time: 4 min
2015/09/20 Share

模块化带来的问题

前端交互的复杂和更新的频繁,对程序的可维护性和健壮性提出了更大的要求。基于本地文件的后端模块化,给了前端模块化很大的启示。模块化开发给维
护和开发带来了很大的好处,每个功能按模块区分,不同功能通过接口沟通,各司其职,各行其道。那么模块化就要求一个功能一个模块,但是js语言本并没有模块化的功能,也无法声明模块的依赖,而且一个模块一个请求,这在分秒必争的浏览器显然是一个逆发展。总的来说,模块化需要解决以下问题:

  • 模块的声明定义和接口的导出
  • 模块的依赖机制
  • 资源的优化整合

解决了以上问题,才能让模块化成为可能。

如何模块化

模块的声明定义和接口的导出

js语言里本身不具备模块的功能,没关系,硬的没有,可以来软的。通过模拟实现模块的功能,以seajs为例,seajs的模块定义如下:

1
2
3
4
5
6
7
8
define("ModuleA", function(require, exports){
var moduleB = require("ModuleB");
return {
test: function(){
console.log("test");
}
}
})

seajs通过define函数实现模块定义功能,该函数实际上是定义了一个模块名称为ModuleA的模块,模块的内容为Factory函数,即回调函数。导出的接口导出是return的值,或者可以通过exports变量实现导出。

模块的依赖机制

模块的依赖指的是模块里面引用了别的模块,所依赖的模块与自身模块的关系维护。加载过的模块第二次再次require的时候怎么办?Factory会运行两次?答案是否定的,同一个模块不该运行两次。js里如何发现依赖?在seajs里是通过正则表达式解决的,seajs会将factory函数tostring一下,拿到函数体,再进行正则匹配拿到依赖的模块,然后去加载依赖的模块,如果发现依赖的模块也有依赖,则再起加载~递归地加载。上面的ModuleA加载的时候发现ModuleB需要先加载,则会发起一个ModuleB的加载,在浏览器里seajs是通过生产script标签进行js加载的,监听其onload事件来执行模块注册,每次加载过的模块都会保存一份到seajs.cache里,每次加载都会先判断cache里有没有,没则发起http去获取模块代码,有则看看模块是否已经运行过factory函数,如没有则运行并返回结果,有则直接返回结果。seajs3.0里做了一个优化,每次用script加载完后,都会删除该dom节点以减轻dom树的内存压力。(ps:seajs里是允许循环依赖的,不过并没有解决循环依赖,不过循环依赖这种逻辑上有问题的组织方式本就不该存在。)

模块既然是一个文件,那seajs里是如何标识一个模块的呢?也就是它的模块唯一标识的管理。seajs由于是以网络文件为基础的,它在请求每个模块的时候都会生给模块分配一个id,对与define定义的模块,会加上网络域名的前缀,如果模块本身没有id,则以模块的路径为基础如文件/static/modules/init/init.js的文件,若define的时候没有指定id,直接define(factory),则它会以http://xxx.com:xx/static/modules/init/init.js作为模块的唯一id。值得一提的是,seajs的默认当前工作空间为sea.js文件的当前目录。也就是说require("jquery")的时候,如果jquery不是iyige别名,则它会去sea.js的当前目录里去找jquery文件。

资源的优化整合

模块化增加了文件的数量,而且由于seajs以网络文件为基础,本地找不到模块的时候,就回去加载网络上的模块,所以这也给打包带来很大的挑战。把多个模块合并成一个大文件,这也就放弃了模块当个加载的机会。那一个文件有多个模块的时候,怎么去区分一个模块呢?这就只能通过模块的id了,也就是说构建的时候需要动态生成模块的id。所以合并之前要做一个模块id提取和依赖提取的操作。

seajs的误区

seajs里有几个地方很容易让人引起误解。

  1. seajs是异步加载文件,但默认不是按需加载,模块只要require了,就会被加载。所以我们会看到network里有一些模块我们并没有用到,但是它第一次的时候已经加载了。要让其不加载,做到按需加载,只能调用require.async了。
  2. require函数并不会发起模块文件的请求,模块文件的请求时在入口函数时,通过对factory进行正则匹配的时候发起的;require只是会触发factory的执行和获取接口。

模块化是大势所趋,但seajs的模块化方案只是js的模块化,并不是完全彻底的前端模块化。相比而言react的模块化更加彻底和完整。但是seajs的模块化可以借助构建工具完善。

CATALOG
  1. 1. 模块化带来的问题
  2. 2. 如何模块化
  3. 3. seajs的误区