传统的Nodejs采用Commonjs模块系统,从ES6之后ECMAScript(ESM)模块系统逐渐增多,目前两种模块管理系统仍在交叉使用,都需要有所了解。
1. Commonjs模块导出导入方法
常见的Commonjs的导出模式有命名导出、函数导出类导出和类的实例导出几种。Commonjs导出的关键词是exports或module.exports。
- 命名导出模式示例如下:
//namedexport.js
//命名的导出模式是commonjs的标准模式
exports.info=(msg)=>{
console.log(`info:${msg}`);
}
exports.verbose=(msg)=>{
console.log(`verbose:${msg}`);
}
- 函数导出模式示例如下:
//funcexport.js
//函数导出模式是commonjs的扩展
module.exports=(msg)=>{
console.log(`info:${msg}`);
}
module.exports.verbose=(msg)=>{
console.log(`verbose:${msg}`);
}
- 类导出模式示例如下:
//classexport.js
//类导出模式
class Logger{
constructor(name) {
this.name=name;
}
log(msg){
console.log(`[${this.name}]-${msg}`);
}
info(msg){
this.log(`info:${msg}`);
}
verbose(msg){
this.log(`verbose:${msg}`);
}
}
module.exports=Logger;
- 类的实例导出模式:
//instanceexport.js
//类的实例导出
class Logger{
constructor(name){
this.count=0;
this.name=name;
}
info(msg){
this.count++;
console.log(`[${this.name}] ,count=${this.count}:${msg}`);
}
}
module.exports=new Logger('DEFAULT');
- 通过Commonjs导出模块的导入示例
//命名导出的模块导入方法示例
const logger1=require('./namedexport');
logger1.info('This is a named export example');
logger1.verbose('With a verbose message');
//函数导出的模块导入方法示例
const logger2=require('./funcexport');
logger2('This is a function export example');
logger2.verbose('With a verbose message');
//类导出的模块导入方法示例
const Logger3=require('./classexport');
let logger3=new Logger3('DB');
logger3.info('This is a class export example');
logger3.verbose('With a verbose message');
//类的实例导出的模块导入方法示例
const logger4=require('./instanceexport');
logger4.info('This is a class instance export example');
const logger5=require('./instanceexport');
logger5.info('This is a class instance export example');
//可以通过以下方式向导入的模块添加customMessage方法
require('./namedexport').customMessage=(msg)=>{
console.log(`This is a patched msg :${msg}`);
}
logger1.customMessage('logger 1 is patched');
2. ECMAScript模块导入与导出的方法
ESMAScript方式仅在较新的Nodejs版本中受支持,此外,Nodejs默认采用Commonjs语法导入导出模块,直接使用ECMAScript模块会报错,在文件对应的package.json
中添加"type":"module",
字段,才能正常使用ECMAScript模块。而一旦确定了type
字段后,就不能再使用Commonjs语法,需要参考下一节的内容进行调整。
- ECMAScript模块导出
//esmexport.js
//采用ESM导出示例
//导出函数
export function defaultlog(msg){
console.log(msg);
}
//导出定义的常数
export const DEFAULT_VALUE='info';
export const LEVELS={
error:0,
debug:1,
warn:2,
data:3,
infor:4,
verbose:5
}
//导出类
export class DefaultClass{
constructor(name){
this.name=name;
}
log(msg){
console.log(`[${this.name}]:${msg}`);
}
}
- 默认导出
//defaultexport.js
//默认类导出
export default class Logger{
constructor(name){
this.name=name;
}
log(msg){
console.log(`[${this.name}]:${msg}`);
}
}
- 模块导入示例
// 导入整个模块
import * as loggerModule from './esmexport.js';
console.log(loggerModule);
const defaultClass=new loggerModule.DefaultClass('ESMA');
defaultClass.log(`test info,DEFAULTVALUE is ${loggerModule.DEFAULT_VALUE}`);
//导入模块的某个对象
import { defaultlog } from './esmexport.js';
defaultlog('esma export example');
//导入默认的模块,自定义Logger2
import Logger2 from './defaultexport.js';
const logger2 = new Logger2('default');
logger2.log('sample message');
//导入其他目录下的Commonjs模块(在同一个目录下会失败)
import { info as newinfo,verbose as newverbose } from '../namedexport.js';
newinfo('newinfo message');
newverbose('new verbose message');
//命名导出的模块导入方法示例
//将.js后缀改为.cjs,可以让node将该文件当作Commonjs解析,可以通过import进行导入
//类似地,如果要让Nodejs将一个文件当作ECMAScript,需要将.js后缀修改为.mjs
import logger1 from './namedexport2.cjs';
logger1.info('This is a named export example');
logger1.verbose('With a verbose message');
3. 模块异步导入导出
Nodejs支持模块的异步导入与导出,本例通过一个根据命令行参数选择模块导入来进行说明。
创建el.js
、en.js
等文件。
//el.js希腊语
export const HELLO='Γειά σου Κόσμε';
//en.js英语
export const HELLO='Hello world';
//es.js西班牙语
export const HELLO='Hola mundo';
//it.js意大利语
export const HELLO='Ciao mondo';
//pl.js波兰语
export const HELLO='Witaj świecie';
异步导入模块
//根据命令行参数选择解析的语言,例如
// node ./main.js el
const SUPPORTED_LANGUAGES=['el','en','es','it','pl'];
const selectedLanguage=process.argv[2];
if(!SUPPORTED_LANGUAGES.includes(selectedLanguage)){
console.error(`The selected language ${selectedLanguage} is not supported yet`);
process.exit(0);
}
const translationModule=`./${selectedLanguage}.js`;
import(translationModule)
.then((str)=>{ //str是导入的模块
console.log(`Your selected language is ${selectedLanguage} \n ${str.HELLO}`)
})
4. Commonjs和ECMAScript部分区别
ECMAScript是在严格模式下运行的,他也不支持Commonjs提供的一些引用,例如require,exports,__filename,__dirname。如果要使用这些内容,需要进行替换。
import {fileURLToPath} from 'url';
import {dirname} from 'path';
const __filename=fileURLToPath(import.meta.url);
const __dirname=dirname(__filename);
5. TypeScript的情况
TypeScript是微软主导的基于JavaScript的语言,可用于更严谨完整的编写JavaScript程序。在TypeScript中主要使用两种导入导出的方式,分别继承自ECMAScript和兼容Commonjs。使用export
关键词导出的模块,使用import
导入,而使用export=
导出的模块则需要通过import Module=require('module')
。在TypeScript官方文档(或中文文档)可以看到关于导入导出的详细示例。
6.示例代码说明
COMMON,ECMA、async和TS文件夹分别包含签署的Commonjs,ECMA,异步和Typescript模式下导入导出的代码示意。各目录下main文件为模块导入与测试,其他文件为不同的导出方法示意。
示意代码下载:
链接:https://pan.baidu.com/s/1C9YM5JEEITLnxOux3WILhw?pwd=7oyw
提取码:7oyw