Node.js模块化

Node.js 模块化

介绍

模块化与模块

将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为模块化。其中拆分的每一个文件就是模块,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用。

  • 在 Node.js 中,文件就是模块,每个.js.json文件都可以被看作是一个独立的模块。
  • Node.js 会缓存加载过的模块,这意味着如果同一个模块被多次请求,它只会在第一次加载时执行,后续请求将直接从缓存中读取。
  • 每个模块都有自己的局部变量和函数作用域,这有助于防止全局命名空间的污染。
导入与导出
  • 导出:使用 module.exports 对象来暴露模块中的函数、对象或值。例如:
// myModule.js
function sayHello() {
  console.log('Hello!');
}

module.exports = {
  sayHello: sayHello
};
 // or
 exports.sayHello = sayHello;
  • module.export可以暴露任意数据
  • 不能使用exports = value的形式暴露数据(比如exports = ‘1’,打印的结果会是空对象{}),模块内部module与exports的隐式关系 exports = module.exports = {}

举例说明:

// a.js
const b = require('./b');
console.log(b) // 输出{}
// b.js
exports= '1';
console.log(module.exports) // 输出{}
console.log(exports === module.exports) // 输出true

repuire返回的结果是目标模块的module.exports的值,并不是exports的值,因为require机制设计的本质就是返回模块的导出值,而这个导出值正是由module.exports确定的。

举例说明

c.js
exports.greeting = 'Hello,would';
// 等于
exports.module.greeting = 'Hello,would';

当你在另一个文件中使用require加载c.js时:

const c = require(./c);
console.log(c.greeting) //  输出 "Hello, world!"

上面的例子中,c变量将指向c.js文件中module.exports对象的内容。
可以像图示这样理解:
在这里插入图片描述

然而,需要注意的是,如果在模块中直接将module.exportsexports重新赋值给一个新的对象或值,这将切断它们之间的默认关联。例如:

// myModule.js
module.exports = { greeting: "Hello, world!" };
// 或者
exports = { greeting: "Hello, world!" };

在上述任何一种情况下,如果使用exports进行重新赋值,则module.exports将不再被更新,反之亦然。因此,在实际开发中,通常推荐直接操作module.exports以避免意外的副作用。这样可以确保无论何时使用require,都会获得准确的模块导出值

  • 导入:使用 require()函数来引入其他模块。例如:
// app.js
const myModule = require('./myModule');

myModule.sayHello();
  • 默认导出单个实体
// myModule.js
export default function sayHello() {
  console.log('Hello!');
}
  • 导入
// app.js
import sayHello from './myModule';
sayHello();

Node.js的导入/导出语法默认遵循CommonJS规范,对于ES6的import/export语法的支持是在Node.js v14.5.0及更高版本中添加的,并且需要在文件扩展名或文件顶部指定type="module"

require使用的一些注意事项:

  1. 自己创建的模块,导入路径时建议写相对路径,且不能省略./../
  2. js和json文件导入时可以不用写后缀,如果出现文件名相同,则js优先
  3. 如果导入其他类型的文件,会以js文件进行处理,如果导入的路径是个文件夹,则会先去检测该文件夹下package.json文件中的main属性对应的文件,存在则导入,反之则报错,如果main属性不存在,或者package.json不存在,则会尝试导入文件夹在的index.js和index.json,如果找不到,则报错。

文件树:

│  main.js
│  
└─modules
        app.js
        package.json
// mian.js
const greeting = require('./modules');
console.log(greeting); // 打印 hello
// app.js
module.exports = 'hello';
// pageage.json
{
	"main": "./app.js"
}
  1. 导入 node.js 内置模块时,直接require模块名即可,无需.././

导入模块的基本流程

介绍require导入自定义模块的基本流程

// 新建greeting.js
module.exports = 'Hello World!';
  1. 将相对路径转为绝对路径,定位目标文件
// 引入模块
const greeting = require('./greeting.js');
// 伪代码
function require(file) {
    // 1. 将相对路径转为绝对路径
    const absolutePath = path.resolve(__dirname, file);
}

// 引入模块
const greeting = require('./greeting.js');
  1. 缓存检测
function require(file) {
    // 1. 将相对路径转为绝对路径
    const absolutePath = path.resolve(__dirname, file);

    // 2. 缓存检测
    if(caches[absolutePath]) {
        return caches[absolutePath];
    }
}

const greeting = require('./greeting.js');
  1. 读取目标文件代码
function require(file) {
    // 1. 将相对路径转为绝对路径
    const absolutePath = path.resolve(__dirname, file);

    // 2. 缓存检测
    if(caches[absolutePath]) {
        return caches[absolutePath];
    };
    
    // 3. 读取目标文件代码 readFileAsync返回的是buffer,用toString转换一下
    let code = fs.readFileAsync(absolutePath).toString();
}

const greeting = require('./greeting.js');
  1. 包裹为一个函数并执行(自执行函数)。通过arguments.callee.toString()查看自执行函数
// 修改greeting.js
const greeting = {
    info: 'Hello World!'
}

module.exports = greeting;

// 输出 arguments.callee指向函数 toString可以看到函数的代码体
console.log(arguments.callee.toString());

打印结果

function require(file) {
    // 1. 将相对路径转为绝对路径
    const absolutePath = path.resolve(__dirname, file);

    // 2. 缓存检测
    if (caches[absolutePath]) {
        return caches[absolutePath];
    };

    // 3. 读取目标文件代码
    let code = fs.readFileAsync(absolutePath).toString();

    // 4. 包裹为一个函数并执行(自执行函数)。通过`arguments.callee.toString()`查看自执行函数
    const module = {};  // 实参1
    const exports = {}; // 实参2
    (function (exports, require, module, __filename, __dirname) {
        const greeting = {
            info: 'Hello World!'
        }

        module.exports = greeting;

        // 输出
        console.log(arguments.callee.toString());
    })(exports, require, module, __filename, __dirname); // 修改为自执行函数 需要传入实参1,2
}

const greeting = require('./greeting.js');
  1. 缓存模块的值
function require(file) {
    // 1. 将相对路径转为绝对路径
    const absolutePath = path.resolve(__dirname, file);

    // 2. 缓存检测
    if (caches[absolutePath]) {
        return caches[absolutePath];
    };

    // 3. 读取目标文件代码
    let code = fs.readFileAsync(absolutePath).toString();

    // 4. 包裹为一个函数并执行(自执行函数)。通过`arguments.callee.toString()`查看自执行函数
    const module = {};  // 实参1
    const exports = {}; // 实参2
    (function (exports, require, module, __filename, __dirname) {
        const greeting = {
            info: 'Hello World!'
        }

        module.exports = greeting;

        // 输出
        console.log(arguments.callee.toString());
    })(exports, require, module, __filename, __dirname); // 修改为自执行函数 需要传入实参1,2

		// 5. 缓存结果值
   		 caches[absolutePath] = module.exports;
}

const greeting = require('./greeting.js');
  1. 返回module.exports的值
function require(file) {
    // 1. 将相对路径转为绝对路径
    const absolutePath = path.resolve(__dirname, file);

    // 2. 缓存检测
    if (caches[absolutePath]) {
        return caches[absolutePath];
    };

    // 3. 读取目标文件代码
    let code = fs.readFileAsync(absolutePath).toString();

    // 4. 包裹为一个函数并执行(自执行函数)。通过`arguments.callee.toString()`查看自执行函数
    const module = {};  // 实参1
    const exports = {}; // 实参2
    (function (exports, require, module, __filename, __dirname) {
        const greeting = {
            info: 'Hello World!'
        }

        module.exports = greeting;

        // 输出
        console.log(arguments.callee.toString());
    })(exports, require, module, __filename, __dirname); // 修改为自执行函数 需要传入实参1,2

		// 5. 缓存结果值
   		 caches[absolutePath] = module.exports;

		// 6. 返回
		retrun module.exports;
}

const greeting = require('./greeting.js');

模块化项目

编码时是按照一个一个编码,整个项目就是一个模块化的项目

模块路径
  • 相对路径: 如果你引用的是项目内的模块,可以使用相对路径。
  • 绝对路径: 引用系统模块或安装的第三方模块时,使用绝对路径或模块名称即可。
模块解析

当 Node.js 尝试解析模块路径时,它会遵循以下步骤:

  1. 检查是否为内置模块。
  2. 尝试在当前目录下查找模块。
  3. 检查 node_modules 目录。
  4. 查找指定的.js, .json, 或.node 文件。

模块化优点

  1. 防止命名冲突
  2. 高复用性
  3. 高维护性

CommonJS规范

module.exportsexports以及require这些都属于CommonJS模块化规范中的内容,而Node.js是实现了CommonJS模块化规范,二者关系有点像JavaScriptECMASriprt的关系。

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Node.js中,模块化是一种组织和管理代码的方式,它允许将代码拆分成多个模块,使得代码更可维护、可重用和可测试。以下是在Node.js中进行模块化的几种常见方式: 1. CommonJS模块化: - 使用`require`函数引入其他模块,例如:`const module = require('./module');` - 使用`module.exports`导出模块的功能,例如:`module.exports = { ... }` 示例: ```javascript // module.js const greeting = 'Hello'; function sayHello(name) { console.log(`${greeting}, ${name}!`); } module.exports = { sayHello }; ``` ```javascript // main.js const module = require('./module'); module.sayHello('John'); ``` 2. ES模块化: - 使用`import`语句引入其他模块,例如:`import module from './module';` - 使用`export`关键字导出模块的功能,例如:`export function greet(name) { ... }` 注意:ES模块化需要在Node.js版本12及以上才能原生支持。 示例: ```javascript // module.mjs const greeting = 'Hello'; function sayHello(name) { console.log(`${greeting}, ${name}!`); } export { sayHello }; ``` ```javascript // main.mjs import { sayHello } from './module.mjs'; sayHello('John'); ``` 3. 第三方模块: - 使用`npm`命令安装第三方模块,例如:`npm install module-name` - 使用`require`或`import`语句引入第三方模块,例如:`const module = require('module-name')`或`import module from 'module-name'` 示例: ```javascript // main.js const express = require('express'); const app = express(); ``` 这些是在Node.js中进行模块化的常见方式。你可以根据自己的需求选择适合的模块化方法来组织和管理代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值