前言
高能预警,前方山路十八弯
在上一篇文章里简单的讨论了一下模块化Js, 先来回顾一下目前模块化的两大规范:
CommonJs 同步加载模块规范
AMD/CMD 异步加载模块规范
其中CMD规范的产出是国内目前十分火爆的SeaJs, 这篇文章主要是解释几个使用SeaJs会碰到的重要概念
具名模块
匿名模块
路径即ID原则
匿名模块
SeaJs定义匿名模块一般采用如下的方式:
define(function(require,exports,module){xx})
具名模块
SeaJs定义除了定义匿名模块,还可以定义具名模块
//define(BlockID,[Deps],function(require,exports,module){})
define(‘A’,[],function(require,exports,module){xx})
其中可以
第一个参数 定义该模块的名字(即ID),用来唯一标识该模块
第二个参数 把该模块依赖的模块从函数体里提到参数中,用来标识该模块还依赖了哪些模块
第三个参数 模块主体
为什么需要具名模块?
诚然,我们可以把所有模块都以匿名的形式书写。但这样有个很大的缺点,就是模块化会导致Js文件特别多,这样无形中会加大了http请求的数
我们在知道,在文件比较小的时候, 文件的大小并不显著影响http的下载速度, 但是如果把这个文件拆成两个文件下载,增加的一次http开销确是很大的
所以,很多情况下我们需要把零碎的Js模块进行合并成一个文件。但是这么多模块合并在一个文件里,全是匿名的话,系统如何区别哪个是哪个模块呢? 因此,我们需要对这些模块给不同的ID进行标识。 于是这些带了ID的模块,就叫做具名模块
路径即ID
上面解释了什么是具名模块,为什么需要用具名模块,我们现在就来给具名模块命名
SeaJs遵循的是路径即ID命名规则,意思就是具名模块的ID名是路径的一部分. 而沿着最终拼接出来的路径,肯定可以找得到这个具名模块
听起来很绕,但这个规则非常非常非常重要.作者也是在理解SeaJs路径的过程中踩坑无数,这里就来重点讲一下
首先来看SeaJs的路径的书写种类,总的来说,可以分为3种.
相对路径(以相对路径符开头,比如
../js/
), 路径以本页面为起点直接路径(以直接目录开头,比如
js/
), 路径以baseUrl为起点根路径(以根路径开头, 比如
/app/js/
) 路径以项目根目录为起点
这里拿入口文件index.html举例:
<!--例1-->
<script>
seajs.config({
base: "../js/",
alias:{"JQ":"lib/jquery"}
})
seajs.use("src/index.js")
</script>
base
参数是以当前页面(index.html)为参照,设定基础相对路径baseUrl.
在本例中base
设定采用相对路径的形式, 那么baseUrl = (index.html的位置) + (../js/)
alias
能够给具名模块(例子中该匿名模块ID为lib/jquery)取别名(JQ),取别名的好处在于可以把具名模块本身非常冗长的路径命名变得很短很小清新,一般是针对页面需要引用的库文件.
在本例中JQ 后的路径是采用直接路径的形式, 那么JQ的路径=(baseUrl) + (lib/jquery) = (index.html的位置)+ (../js/) + (lib/jquery/
)sea.use
用来指定SeaJS加载器的入口, 通过在入口js再加载七七八八页面所需的JS模块达到按需加载的目的
在本例中,sea.use的路径是采用直接路径的形式, 那么入口文件index.js的路径 =(baseUrl) + (src/index.js) = (index的位置) + (../js/) + (src/index.js)
如果换一种形式写:
<!--例2-->
<script>
seajs.config({
base: "../js/",
alias:{"JQ":"../js/lib/jquery"}
})
seajs.use("../js/src/index.js")
</script>
base设定采用相对路径的形式, 那么baseUrl =
(index.html的位置) + (../js/)
alias JQ 后的路径是采用相对路径的形式, 那么JQ的路径=
(index.html的位置) + (../js/lib/jquery)
sea.use的路径是采用相对路径的形式, 那么入口文件index.js的路径=
(index.html的位置) + (../js/src/index.js)
现在拿例1来说:
<!--例1-->
<script>
seajs.config({
base: "../js/",
alias:{"JQ":"lib/jquery"}
})
seajs.use("src/index.js")
</script>
既然alias中具名模块叫lib/jquery, 那么你的jquery通过define定义的模块ID一定是lib/jquery
define(‘lib/jquery’,[],function(require,exports,module){xx})
又由于alias
中具名模块(lib/jquery)
的采用了直接路径的方式, 根据路径即ID的原则,你应该可以顺着 (baseUrl) + (lib/jquery)
找到该具名模块的位置,如果找不到,肯定会报错
现在拿例2来说:
<!--例2-->
<script>
seajs.config({
base: "../js/",
alias:{"JQ":"../js/lib/jquery"}
})
seajs.use("../js/src/index.js")
</script>
既然alias中具名模块叫../js/lib/jquery, 那么你的jquery通过define定义的模块ID一定是../js/lib/jquery
define(‘../js/lib/jquery’,[],function(require,exports,module){xx})
又由于alias
中具名模块(../js/lib/jquery)
的采用了相对路径的方式, 根据路径即ID的原则,你应该可以顺着 index.html当前位置 + (../js/lib/jquery)
找到该具名模块的位置,如果找不到,肯定会报错
实际使用
但实际的使用上,我们基本不会去写具名模块, 而是全部写匿名模块
然后通过自动化构建工具(比如grunt,gulp,fis3)的插件去自动解决匿名模块具名化的问题,比如grunt就提供了相关插件:
grunt-cmd-transport 将匿名模块转换成具名模块
grunt-cmd-concat 将具名模块合并压缩到一个Js文件里
结语
SeaJs大法好,Grunt大法好 但在使用这些工具的之时,并不是简单抄一两个demo就完事. 很多情况下你要根据自己工程的特性来调整目录结构,而模块所在的路径
,和模块的ID
, 和最后JS合并压缩
的过程息息相关. 所以必须理解它们的规则,运用起来才能更加得心应手