1、javascript模块化
javascript 模块化是随着 javascript 的工作量逐步增加而产生的必然结果,近年来,javascript 模块化已经诞生了部分标准,也已经在下一代的 javascript 官方标准里提出了草案。
目前,主流的模块化规范是 AMD、CommonJS等,他们良好的规范,优雅的接口设计,合理的设计模式被很多前端开发工程师所接受,并且也有很多模块化加载器也支持他们,例如 requireJS、seaJS等。
2、javascript模块加载器
2.1、coolie 优势
- coolie (苦力)是作者在近期书写的一款模块加载器,相比市面上的其他模块加载器,有以下优势:
- 轻量,良好的身材,仅支持高级现代浏览器,没有繁多的路径别名,和 NodeJS 一样,仅支持本地模块。
- 小巧,尽量少的接口(2个接口)和配置(2个配置)。
- 优雅,合理的接口和配置。
- 安全,尽量不侵入全局变量(2个,分别是 define 和 coolie),放心编程。
- 方便,配套的发布构建工具,为之量身打造。
- 省心,一键构建,无须人工干预,没有繁多和难以理解的构建配置。
2.2、本地开发环境
开发环境下,不显式模块ID和模块依赖,模块路径必须写完整:
模块入口:./index.js
// 开发环境不能写模块ID
define(function (require){
// 模块依赖,不能省略文件后缀
var num = require('./abc.js');
var text = require('text!./def.txt');
alert(num + text);
// => "123这里是一串纯文本"
});
依赖模块:./abc.js
// 开发环境不能写模块ID
define(function (require, exports, module){
// 模块导出
module.exports = 123;
});
依赖文本:./def.txt
这里是一串纯文本
2.3、生产环境下:
发布之后,模块入口:./index.js?v=abc123
define('0', ['1', '2'], function (a){
var b = a('1');
var c = a('2');
alert(b+c);
// => "123这里是一串纯文本"
});
define('1', [], function (a, b, c){
c.exports = 123;
});
define('2', [], function (a, b, c){
c.exports = '这里是一串纯文本';
});
2.4、coolie配置
coolie.config({
// * 入口模块的基准路径,也可以写绝对路径
// `base`是相对于`coolie.js`所在的路径的,因此只要`coolie.js`路径不变,配置文件无论被哪个文件引用都没关系
// 可选,默认为`coolie.js`所在的路径
base: './',
// * 模块文件版本,用于清除文件缓存,常用于发布到生产环境上,具体细节查看`coolie`发布工具
// 比如入口文件是`index.js`,那么实际请求的路径为`/path/to/index.js?_=abc123`
// 可选,默认为空
version: 'abc123'
});
注意点
- base参数配置,与sea.js有些出入,请注意。
- base参数是相对于coolie.js所在路径的。
- base参数也是入口模块的相对路径。
- version版本号在开发环境下可以为空,构建时会自动更新版本号以便清除生产环境下的缓存。
2.5、coolie 使用
// 运行入口模块路径,注意这里没有回调
// 模块路径相对于`config.base`
coolie.use('./index.js');
注意点
- 必须手动调用.use()方法。
- 参数是入口模块路径,不是入口模块名称。
- 为了配置文件的重用性,当coolie.js所在的script指定了
data-main
属性,内联属性优先级最高,因此.use参数此时可以为空。 如:<script src="./coolie.js" data-main="./index2.js"></script>
,此时入口模块就为相对于config.base
的./index2.js文件。 此种情况会在控制台输出提示。
3、模块构建工具
3.1、介绍
配套的模块构建工具 coolie(苦力),已在 npmjs 官方上线。作者之前也使用过 spm3 打包,感觉还是太复杂了,后来自制了 apb,再后来就索性自己写了一整套东西,包括模块加载器和模块构建工具。尽量保持它们的良好身材,只做它们擅长和分内的事情。
3.2、优势
相比其他构建工具,他有以下优势:
- 优雅,大众化和易理解的API。
- 易用,全部自动生成配置文件,每一步都有详细说明。
- 安全,不修改任何源文件。
- 极致,全部模块,压缩所有可压缩的内容,包括模块ID、模块依赖路径、局部变量等。
3.3、特点
更多API说明,请移步 github 项目,链接如下。
4、链接
- coolie模块加载器:https://github.com/cloudcome/coolie
- coolie模块构建工具:https://github.com/cloudcome/nodejs-coolie
觉得构建很奇怪,模块id是0,1,2这样的序号,那么这是要求页面上只能use一个js不然会有模块id重名?
连核心匹配的正则都是seajs的,还是乖乖用seajs吧,方便
- 构建之后模块ID会变成0-z的单个字符。
- 一个页面只能有一个入口模块,减少了复杂度,这样更加的纯粹。
cube 来围观 ,https://github.com/node-cube/cube AMD,CMD写起来多类,要像node一样写,才省心
@cloudcome 明白这样做的意图。但是实际上会增加了迁移成本。当然只是应用于新页面倒没什么问题。
@fish 挺好的。CommonJS 在浏览器端开发有哪些小不足?
- 无法完美调试,在某些浏览器下无法正确显示脚本路径,甚至丢失断点。
- 需要额外包装一层,虽然有加载器自动包裹。
- cude 里用到了
node_modules
里面的模块?会不会造成混淆和误解?
前后端虽然都用了 JS,但还是不必趋同,以目前的情形来看。前端用 webstorm 写一个 cmd 模块,也不差写一个 define
。个人拙见。
@airyland 嗯,是的,毕竟没有适应所有情况的加载器。coolie 尽可能做的更好更多更快,前提是做的事情足够纯粹,而不是各种别名或捷径。更加严谨的约束,对模块开发来说,势必会更加顺畅。
@cloudcome 必须完美支持调试断点, 混淆模块的问题,是个小麻烦,后续考虑 cube 命令封装掉。
这名字起的,作践自己嘛
@leapon 哈哈
@leapon coolie ,这个名字有什么不好吗?
@leapon 哈哈,就是因为这个而取名的。coolie(苦力),模块加载器本身就是一个苦力,把所有模块集合到一起。
在写 coolie 的时候也参考了你的脚本,不过有一处提取 require 的正则不是很完善。
var jsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g;
'/** require("abc"); **/'.match(jsRequireRegExp);
@cloudcome 一看就是木有好好瞧我的代码。。我在匹配之前不是把注释都干掉了么,所以永远不会有这种情况的。。。
https://github.com/litheModule/lithe/blob/master/lithe.js#L105
我还是觉得分成2段来看,比较好维护和直观。
@xiaojue 嗯,我表述有误,贴的代码对你造成了误解,其实我想表达的意思是,有注释的时候,你的正则匹配是错的:
var commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
var jsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g;
'var reg1 = /\\/*/;var abc = require("abc");/*comment*/'.replace(commentRegExp, '').match(jsRequireRegExp);
@cloudcome 你表达的还是不清楚,你把match去掉,你会发现不是require那个regexp导致的,而是去注释那段正则导致的。
所以你只需要贴commentRegExp就好了嘛,而我又发现你的string部分明显又有问题嘛。
你自己拷贝出来执行一下?
正则这东西,如果非要拿极值去测,任何正则都不是完美滴,所以我目前还不会改,我们线上好几个项目都用的lithe,目前还没有遇到过你说的提取出错的问题。
@cloudcome 当然我们的代码都是有注释的~我们还是比较有节操的程序员……
@xiaojue 为什么有2个反斜杠?因为在字符串里的反斜杠是需要反斜杠转义的。实际代码为:
var reg1 = /\/*/;
var abc = require("abc");
/*commets*/
两个正则搭配起来去取依赖: 1、先去注释,结果为:
"var reg1 = /\"
2、提取依赖,显而易见无法提取依赖了。
一开头说的是提取依赖的正则有问题,可能有些喧宾夺主了,其根源是去除注释有问题。
这方面,不凡借鉴下 seajs,它提取依赖的正则暂时是没有发现问题的,并且也不需要事先去删除注释。 而你说,你的项目暂时没有遇到,不过上述情况是会出现的。
例如:https://github.com/madrobby/zepto/blob/master/src/ajax.js#L225-L296, 高亮区域就形成了一个符合注释正则的非注释区域。简要如下:
"setHeader('Accept', mime || '*/*')\n\n\n$.get = function(/* url, data, success, dataType */){".match(commentRegExp)
学习
怎么加载相关的Css?
var css = require('css!./path/to/some.css');
var html = require('html!./path/to/some.html');
var text = require('text!./path/to/some.txt');
var js = require('./path/to/some.js');
这样写死路径,重构的时候是不是很麻烦呢?
@cloudcome some.css
body{
background:yellow;
}
index.js中加载 var css = require(‘css!./some.css’);
刷页面,并没有效果,控制台也没有报错,firebug网络面板也显示加载了some.css
-
Q 路径写死了,以后重构怎么办?
-
A 重构的话,牵扯到路径都很麻烦,这是无法避免的,因为这些路径都是物理路径,不是逻辑的。
-
Q 加载了
some.css
,为什么页面没效果? -
A 模块加载器只加载不渲染,你要对使 CSS 生效,必须自己插入到 DOM 里。
例:
/**
* 添加样式
* @param {String} styleText 样式内容
*
* @example
* modification.importStyle('body{padding: 10px;}');
*/
importStyle: function (styleText) {
var style = this.create('style');
styleText = String(styleText);
this.insert(style, head, 'beforeend');
// IE
if (style.styleSheet !== undefined) {
// 此 BUG 仅影响 IE8(含) 以下浏览器
// http://support.microsoft.com/kb/262161
// if (document.getElementsByTagName('style').length > 31) {
// throw new Error('Exceed the maximal count of style tags in IE')
// }
style.styleSheet.cssText += styleText;
}
// W3C
else {
style.appendChild(this.create('#text', styleText));
}
}
@cloudcome 你说的对,如果遇到你这个情况,中间的require部分是会丢掉的。但是这个情况是只有在代码全部写成一行的情况下。
简化了你的例子:var reg1=///;var abc = require(“abc”);
如果代码写成了一行这种情况,后面的东西是会被认为是注释而去掉。
正则的问题出在 | 后面的部分 也就是 ([^:]|^)//(.*)$ 这一句。
它并没有考虑到 你说的 『字符串里的反斜杠是需要反斜杠转义的』
我这个正则的出处是requirejs,参见:
https://github.com/jrburke/requirejs/blob/master/require.js#L16
看来requirejs也有这个隐患,参见他查找deps的部分:
https://github.com/jrburke/requirejs/blob/master/require.js#L2023
so。。看来需要改改代码啦。seajs的正则写成了一个,对所在行进行了一些限制,我觉得我还是要找到一个能正确处理正则的正则比较靠谱。
找到了一个能正确处理你这个情况的去注释正则方法:
http://james.padolsey.com/javascript/javascript-comment-removal-revisted/
看来要更新一下啦,哈哈哈。