前端模块化开发
其实对前端模块化开发的接触时间并不多,很多见解都是别人的,或者是偏的, 还是乐意记录下来,谁让我一天一个念头
说到前端模块化开发,其实是说 javascript 模块化开发。
目前,大众讲到的 javascript模块化规范 有3种,CommonJS
、AMD
(异步模块定义)、CMD
(通用模块定义)
CommonJS
CommonJS在 node 端模块采用的规范。
根据CommonJS规范,每一个文件都是一个模块,每一个模块都有一个独立的作用域,文件内的变量都是私有的,其他文件不可使用(除非 赋值到 global上)
每个文件对外的接口是 module.exports 对象
。其他文件通过使用这个对象的属性和方法,实现对本文件的使用。
require
用于引用其他模块,实际获得的是其他模块的module.exports
对象。
例子
example.js
var x = 5;
var addX = function(value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
index.js
var example = require('./example.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6
CommonJS模块的特点:
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序。
require内部处理流程
require
实际是 指向当前模块的 module.require
, module.require
又调用Node的 Module._load
(此Module非彼module)
Module._load = function(request, parent, isMain) {
// 1. 检查 Module._cache,是否缓存之中有指定模块
// 2. 如果缓存之中没有,就创建一个新的Module实例
// 3. 将它保存到缓存
// 4. 使用 module.load() 加载指定的模块文件,
// 读取文件内容之后,使用 module.compile() 执行文件代码
// 5. 如果加载/解析过程报错,就从缓存删除该模块
// 6. 返回该模块的 module.exports
};
其中 module.compile()
执行如下:
Module.prototype._compile = function(content, filename) {
// 1. 生成一个require函数,指向module.require
// 2. 加载其他辅助方法到require
// 3. 将文件内容放到一个函数之中,该函数可调用 require
// 4. 执行该函数
};
这是大概的CommonJS 流程
详见CommonJS规范
AMD(异步模块定义)
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
CommonJS 采用的是同步加载机制,如果用于 客户端,必定受到网络的限制。所以,CommonJS不 适用于客户端。
而 AMD 采用的是 模块异步加载 方式,在需要执行到模块文件的时候,实现异步加载,回调执行。
看例子:
//...
require(['math'], function (math) {
math.add(2, 3);
});
//...
当执行到这一段代码的时候, 浏览器会先 加载 math
模块,在math
模块加载成功后, 再执行后面的回调函数 math.add(2,3)
require.js
说道AMD 就不得不提 require.js了。
因为目前要实现 AMD , 不要按照require要求的写法。
首先下载最新require.js ,然后在 html 底部写上如下代码:
<script src="js/require.js" data-main="js/main"></script>
data-main
用于指定网页程序的主模块
main.js
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
// some code here
});
要加载的模块也必须使用特定的写法,使用define()
moduleA.js
define(['moduleD'],function (){
var add = function (x,y){
return x+y;
};
return {
add: add
};
});
CMD(通用模块定义)
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
CMD
跟 AMD
在使用方面非常相似。(其实我没用过)
知呼上的比较
对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:
作者:玉伯
链接:https://www.zhihu.com/question/20351507/answer/14859415
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此处略去 100 行
var b = require('./b') // 依赖可以就近书写
b.doSomething()
// ...
})
// AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
// 此处略去 100 行
b.doSomething()
...
})
就是说, 模块加载完成后, AMD 是立刻执行的,而 CMD是在需要用到的时候才执行的
针对这些不同, 在体现上:
AMD 速度会相对快, 但是会浪费资源
CMD 节省资源, 性能会差一点(反应时间)
webpack
react.js
让 webpack
流行起来。(至少我是通过 react.js
认识到 webpack
的)react.js
可以说是前端(浏览器)项目,可是在编程风格上,确实不折不扣的 CommonJS
风格。webpack
兼容了 CommonJS
和 AMD
。
webpack 是一个模块管理工具。 对于 CommonJS 的模块, 对将其 最终打包在一个js文件里面, 对于不写不需要立刻执行的文件,也可以拆分出来,在运行时异步加载。
当然, 这里并不打算 说明怎么使用 webpack , google上有很多丰富的教程。
巨人的肩膀上,加自己的一点感悟