1、模块/包分类
Node.js 有三类模块,即内置的模块、第三方的模块、自定义的模块。
1.1 内置的模块
Node.js 内置模块又叫核心模块,Node.js安装完成可直接使用。如:
const path = require('path')
var extname = path.extname('index.html')
console.log(extname)
1.2 第三方的Node.js模块
第三方的Node.js模块指的是为了实现某些功能,发布的npmjs.org上的模块,按照一定的开源协议供社群使用。如:
npm install chalk
const chalk = require('chalk')
console.log(chalk.blue('Hello world!'))
1.3 自定义的Node.js模块
自定义的Node.js模块,也叫文件模块,是我们自己写的供自己使用的模块。同时,这类模块发布到npmjs.org上就成了开源的第三方模块。
自定义模块是在运行时动态加载,需要完整的路径分析、文件定位、编译执行过程、速度相比核心模块稍微慢一些,但是用的非常多。
1.3.1 模块定义、接口暴露和引用接口
我们可以把公共的功能 抽离成为一个单独的 js 文件 作为一个模块,默认情况下面这个模块里面的方法或者属性,外面是没法访问的。如果要让外部可以访问模块里面的方法或者属性,就必须在模块里面通过 exports 或者 module.exports 暴露属性或者方法。
m1.js:
const name = 'gp19'
const sayName = () => {
console.log(name)
}
console.log('module 1')
// 接口暴露方法一:
module.exports = {
say: sayName
}
// 接口暴露方法二:
exports.say = sayName
// 错误!
exports = {
say: sayName
}
main.js:
const m1 = require('./m1')
m1.say()
1.3.2 模块的循环引用
由于 exports 使用方式方式不对,会在两个不同 js 循环引用的情况下,导致其中一个 js 无法获取另外一个 js 的方法,从而导致执行报错。如:
a.js
exports.done = false
const b = require('./b.js')
console.log('in a, b.done = %j', b.done)
exports.done = true
console.log('a done')
b.js
console.log('b starting')
exports.done = false
const a = require('./a.js')
console.log('in b, a.done = %j', a.done)
exports.done = true
console.log('b done')
main.js
console.log('main starting')
const a = require('./a.js')
const b = require('./b.js')
console.log('in main, a.done = %j, b.done = %j', a.done, b.done)
main.js 首先会 load a.js, 此时执行到const b = require(’./b.js’);的时候,程序会转去loadb.js, 在b.js中执行到const a = require(’./a.js’); 为了防止无限循环,将a.jsexports的未完成副本返回到b.js模块。然后b.js完成加载,并将其导出对象提供给a.js模块。
我们知道nodeJs的对每个js文件进行了一层包装称为module,module中有一个属性exports,当调用require(‘a.js’)的时候其实返回的是module.exports对象,module.exports初始化为一个{}空的object,所以在上面的例子中,执行到b.js中const a = require(’./a.js’);时不会load新的a module, 而是将已经load但是还未完成的a module的exports属性返回给b module,所以b.js拿到的是a module的exports对象,即:{done:false}, 虽然在a.js中exports.done被修改成了true,但是由于此时a.js未load完成,所以在b.js输出的a module的属性done为false,而在main.js中输出的a module的属性done为true. Nodejs通过上面这种返回未完成exports对象来解决循环引用的问题。
二、CommonJS和ES6 模块化区别
1、创建一个文件夹结构如下
2、文件和文件夹解析:
dist文件夹:用于存放之后打包的文件
src文件夹:用于存放我们写的源文件
main.js:项目的入口文件。具体内容查看下面详情。
mathUtils.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。具体内容查看下面的详情。
info.js: 定义了几个常量
index.html:浏览器打开展示的首页html
3、mathUtils.js文件中的代码:定义两个函数,然后使用commonjs方式导出
function add(num1,num2) {
return num1+num2
}
function mul(num1,num2) {
return num1*num2
}
//CommonJS 模块化导出
module.exports={
add,
mul
}
4、info.js 使用ES6模块化方式导出常量
//ES6 模块化导出
export const name="wangxiaoyu";
export const age=18;
export const height=123;
5、main.js 导入上面两个文件方法和常量
//commonJS 模块化导入
const math=require("./mathUtils.js")
console.log("Hello Webpack");
console.log(math.add(10,20));
console.log(math.mul(10.20));
//ES6 模块化导入
import {name,age,height} from "./info.js"
console.log(name);
console.log(age);
console.log(height);