Node应用由模块组成,采用CommonJS模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他的文件不可见。在服务器端,模块的加载时运行时同步加载;在浏览器端,模块需要提前编译打包处理。
- 所有代码都运行在模块作用域,不会污染全局作用域;
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清楚缓存;
模块加载的顺序,按照其中在代码中出现的顺序;
- 暴露模块:
module.exports = value
或者exports.xxx = value
- 引入模块:
require(xxx)
,如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径。
此处我们有个疑问:
CommonJS暴露的模块到底是什么?
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量时一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
//example.js
var x = 3;
var addX = function(value){
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
上面代码通过module.exports输出变量x和函数addX。
var example = require('./example.js');//如果参数字符串以“./”开头,则表示加载的是一个位于相对路径
console.log(example.x);//3
console.log(example.addX(3));//6
require命令用于加载模块文件。require命令的基本功能是:读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。
CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。这点与ES6模块有重大差异(下文会介绍)举个栗子:
//lib.js
var counter = 2;
function incCounter(){
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
}
上面代码输出内部变量counter和改写这个变量的内部方法IncCounter。
//main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCouncter;
console.log(counter);//2
incCounter();
console.log(counter);//2
上边的代码说明,counter输出以后,lib.js模块内部的变化就影响不到counter了。这是因为counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。
|-modules
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-package.json
{
"name": "commonJS-node",
"version": "1.0.0"
}
npm install uniq --save //用于数组去重
//module1.js
module.exports = {
msg: 'module1',
foo() {
console.log(this.msg)
}
}
//module2.js
module.exports = function() {
console.log('module2')
}
//module3.js
exports.foo = function() {
console.log('foo() module3')
}
exports.arr = [1, 2, 3, 3, 2]
// app.js文件
// 引入第三方库,应该放置在最前面
let uniq = require('uniq')
let module1 = require('./modules/module1')
let module2 = require('./modules/module2')
let module3 = require('./modules/module3')
module1.foo() //module1
module2() //module2
module3.foo() //foo() module3
console.log(uniq(module3.arr)) //[ 1, 2, 3 ]
使用Browserify: Browserify会对代码进行解析,整理出飞马中的所有模块依赖关系,然后把相关的模块代码都打包在一起,形成一个完整的js文件,这个文件中不会存在require这类的模块化语法,变成可以在浏览器中运行的普通JS
|-js
|-dist //打包生成文件的目录
|-src //源码所在的目录
|-module1.js
|-module2.js
|-module3.js
|-app.js //应用主源文件
|-index.html //运行于浏览器上
|-package.json
{
"name": "browserify-test",
"version": "1.0.0"
}
npm install browserify -g
npm install browserify --save-dev
browserify js/src/app.js -o js/dist/bundle.js
<script type="text/javasctipt" src="js/dist/bundle.js"></script>