Skip to content

07 Node.js 模块化

知识点自测

  1. 以下代码运行的结果是多少?

    js
    const arr = [10, 20, 30];
    const result = arr.map((val) => val + 1).reduce((sum, val) => (sum += val), 0);
    console.log(result);
    • A. 60
    • B. 63
    答案
    • B 选项正确
  2. 以下代码运行的结果是多少?

    js
    const obj = { username: 'itheima', password: '777777' };
    const { uname } = obj;
    console.log(uname);
    • A. itheima
    • B. undefined
    答案
    • A 选项正确

模块化简介

了解模块化概念和好处,以及 CommonJS 标准语法导出和导入

  1. 在 Node.js 中每个文件都被当做是一个独立的模块,模块内定义的变量和函数都是独立作用域的,因为 Node.js 在执行模块代码时,将使用如下所示的函数封装器对其进行封装

    js
    (function (exports, require, module, __filename, __dirname) {
      // Module code actually lives in here
      // 模块代码实际上存在于此处
    });
  2. 而且项目是由多个模块组成的,每个模块之间都是独立的,而且提高模块代码复用性,按需加载,独立作用域

    392e0783-2260-4db9-82f0-89a315c976bf

  3. 但是因为模块内的属性和函数都是私有的,如果对外使用,需要使用标准语法导出和导入才可以,而这个标准叫 CommonJS 标准,接下来我们在一个需求中,体验下模块化导出和导入语法的使用

  4. 需求:定义 utils.js 模块,封装基地址和求数组总和的函数,导入到 index.js 使用查看效果

    3a3b724b-35fa-457c-b63b-044899a1086b

  5. 语法

    js
    // 导出
    module.exports = {
      对外属性名:模块内私有变量
    }
    
    //导入
    const 变量名 = require('模块名或路径');
    // Node.js 环境内置模块直接写模块名(例如:fs,path,http)
    // 自定义模块:写模块文件路径(例如:./utils.js)

    导入时变量名的值接收的就是目标模块导出的对象

js
/**
 * 目标:基于 CommonJS 标准语法,封装属性和方法并导出
 */

const baseURL = 'http://hmajax.itheima.net';
const getArraySum = (arr) => arr.reduce((sum, item) => (sum += item), 0);

module.exports = {
  url: baseURL,
  arraySum: getArraySum,
};
js
/**
 * 目标:基于 CommonJS 标准语法,导入工具属性和方法使用
 */

const utils = require('./utils');
console.log(utils);

const apiUrl = utils.url;
console.log(apiUrl);

const sum = utils.arraySum([1, 2, 3, 4, 5]);
console.log(sum);

// ❯ node .\07.commonjs\index.js
// { url: 'http://hmajax.itheima.net', arraySum: [Function: getArraySum] }
// http://hmajax.itheima.net
// 15

小结

  1. Node.js 中什么是模块化?

    • 每个文件都是独立的模块
  2. 模块之间如何联系呢?

    • 使用特定语法,导出和导入使用
  3. CommonJS 标准规定如何导出和导入模块?

    • 导出:module.exports = {}
    • 导入:require('模块名或路径')
  4. 模块名/路径如何选择?

    • 内置模块:写名字。例如:fspathhttp 等。
    • 自定义模块:写模块文件路径,例如:./utils.js

ECMAScript 标准 - 默认导出和导入

掌握 ECMAScript 标准语法中,默认导出和导入的使用

  1. CommonJS 规范是 Node.js 环境中默认的,后来官方推出 ECMAScript 标准语法,我们接下来在一个需求中,体验下这个标准中默认导出和导入的语法要如何使用

  2. 需求:封装并导出基地址和求数组元素和的函数,导入到 index.js 使用查看效果

  3. 语法

    js
    // 导出
    export default {
      对外属性名:模块内私有变量
    }
    
    // 导入
    import 变量名 from '模块名或路径';

    注意

    导入时变量名的值接收的就是目标模块导出的对象

  4. 注意:Node.js 默认只支持 CommonJS 标准语法

    • 如果想要在当前项目环境下使用 ECMAScript 标准语法,请新建 package.json 文件设置 type: 'module' 来进行设置

      js
      {
        "type": "module"
      }
js
/**
 * 目标:基于 ECMAScript 标准语法,封装属性和方法并"默认"导出
 */
const baseURL = 'http://hmajax.itheima.net';
const getArraySum = (arr) => arr.reduce((sum, item) => (sum += item), 0);

export default {
  url: baseURL,
  arraySum: getArraySum,
};
js
/**
 * 目标:基于 ECMAScript 标准语法,"默认"导入,工具属性和方法使用
 */
import utils from './utils.js';
console.log(utils);

const apiUrl = utils.url;
console.log(apiUrl);

const sum = utils.arraySum([1, 2, 3, 4, 5]);
console.log(sum);

// ❯ node .\08.ecmascript\index.js
// { url: 'http://hmajax.itheima.net', arraySum: [Function: getArraySum] }
// http://hmajax.itheima.net
// 15
json
{
  "type": "module"
}

小结

  1. ECMAScript 标准规定如何默认导出和导入模块?

    • 导出:export default {}
    • 导入:import 变量名 from '模块名或路径'
  2. 如何让 Node.js 切换模块标准为 ECMAScript?

    • 运行模块所在文件夹
    • 新建 package.json 内容为 {"type": "module"}

ECMAScript 标准 - 命名导出和导入

掌握 ECMAScript 标准语法中,命名导出和导入的使用

  1. ECMAScript 标准的语法有很多,常用的就是默认和命名导出和导入,这节课我们来学习下命名导出和导入的使用

  2. 需求:封装并导出基地址和数组求和函数,导入到 index.js 使用查看效果

  3. 语法

    js
    // 命名导出
    export 修饰定义语句
    
    // 命名导入
    import { 同名变量 } from '模块名或路径';

    注意

    注意:同名变量指的是模块内导出的变量名

  4. 与默认导出如何选择:

    • 按需加载,使用命名导出和导入
    • 全部加载,使用默认导出和导入
js
/**
 * 目标:基于 ECMAScript 标准语法,封装属性和方法并"命名"导出
 */
export const baseURL = 'http://hmajax.itheima.net';
export const getArraySum = (arr) => arr.reduce((sum, item) => (sum += item), 0);
js
/**
 * 目标:基于 ECMAScript 标准语法,"命名"导入,工具属性和方法使用
 */

import { baseURL, getArraySum } from './utils.js';
console.log(baseURL);
console.log(getArraySum);
console.log(getArraySum([1, 2, 3, 4, 5]));

// ❯ node .\09.ecmascript\index.js
// http://hmajax.itheima.net
// [Function: getArraySum]
// 15
json
{
  "type": "module"
}

小结

  1. Node.js 支持哪 2 种模块化标准?

    • CommonJS 标准语法(默认)
    • ECMAScript 标准语法
  2. ECMAScript 标准,命名导出和导入的语法?

    • 导出:export 修饰定义的语句
    • 导入:import { 同名变量 } from '模块名或路径'
  3. ECMAScript 标准,默认导出和导入的语法?

    • 导出:export default {}
    • 导入:import 变量名 from '模块名或路径'

包的概念

了解 Node.js 环境中包的概念

  1. 包:将模块、代码、其他资料整合成一个文件夹,这个文件夹就叫包

  2. 包分类:

    • 项目包:主要用于编写项目和业务逻辑
    • 软件包:封装工具和方法进行使用
  3. 包要求:根目录中,必须有 package.json 文件(记录包的清单信息)

    json
    {
      "name": "cz_utils", // 软件包名称
      "version": "1.0.0", // 软件包当前版本
      "description": "一个数组和字符串常用工具方法的包", // 软件包简短描述
      "main": "index.js", // 软件包入口点
      "author": "itheima", // 软件包作者
      "license": "MIT" // 软件包许可证(商用后可以用作者名字宣传)
    }
  4. 包使用:

    • 在引入一个包文件夹到代码中,默认引入的是包文件节下的 index.js 模块文件里导出的对象
    • 如果没有 index.js 文件,则会引入 package.jsonmain 属性指定的文件模块导出的对象
  5. 需求:封装数组求和函数的模块,封装判断用户名和密码长度函数的模块,形成一个软件包,并导入到 index.js 中使用查看效果

js
/**
 * 本文件是,utils 工具包的唯一出口
 * 作用:把所有工具模块方法集中起来,统一向外暴露
 */

const { getArraySum } = require('./lib/arr');
const { checkUser, checkPwd } = require('./lib/str');

module.exports = {
  getArraySum,
  checkUser,
  checkPwd,
};
json
{
  "name": "cz_utils",
  "version": "1.0.0",
  "description": "一个数组和字符串常用工具方法的包",
  "main": "index.js",
  "author": "itheima",
  "license": "MIT"
}
js
/**
 * 目标:导入 utils 软件包,使用里面封装的工具函数
 */

const utils = require('./utils');
console.log(utils);

console.log(utils.getArraySum([1, 2, 3, 4, 5]));

console.log(utils.checkUser('admin'));
console.log(utils.checkUser('jinitaimei~'));

console.log(utils.checkPwd('12345'));
console.log(utils.checkPwd('123456789'));

// ❯ node .\10.package\index.js
// {
//   getArraySum: [Function: getArraySum],
//   checkUser: [Function: checkUserName],
//   checkPwd: [Function: checkPassWord]
// }
// 15
// false
// true
// false
// true

小结

  1. 什么是包?

    • 将模块、代码、其他资料聚合成的文件夹
  2. 包分为哪 2 类呢?

    • 项目包:编写项目代码的文件夹
    • 软件包:封装工具和方法供开发者使用
  3. package.json 文件的作用?

    • 记录软件包的名字,作者,入口文件等信息
  4. 导入一个包文件夹的时候,导入的是哪个文件?

    • 默认 index.js 文件,或者 main 属性指定的文件

npm 软件包管理器

掌握使用 npm 管理软件包

  1. npm:软件包管理器,用于下载和管理 Node.js 环境中的软件包

    • npm 是 Node.js 的标准包管理器
    • 据报道,在 2022 年 9 月,npm 注册表中列出了超过 210 万个软件包,使其成为地球上最大的单一语言代码存储库,而且你可以确定(几乎)所有一切都有软件包。
    • 它最初是作为一种下载和管理 Node.js 包依赖的方式,但后来成为前端 JavaScript 中也使用的工具。
  2. npm 使用步骤:

    • 初始化清单文件:npm init -y(得到 package.json 文件,有则跳过此命令)

      注意

      -y 就是所有选项用默认值,所在文件夹不要有中文/特殊符号,建议英文和数字组成,因为 npm 包名限制建议用英文和数字或者下划线中划线

    • 下载软件包:npm i 软件包名称

    • 使用软件包

  3. 需求:使用 npm 下载 dayjs 软件包到本地项目文件夹中,引入到 index.js 中格式化日期打印,运行观察效果

  4. 具体使用流程图:

    0a9b4b3a-268c-47e7-8bd3-f92eb354477a

js
/**
 * 目标:使用 npm 下载 dayjs 软件包来格式化日期时间
 *  1. (可选)初始化项目清单文件,命令:pnpm init
 *  2. 下载软件包到当前项目,命令:pnpm i dayjs
 *  3. 使用软件包
 */

const dayjs = require('dayjs');
console.log(dayjs().format('YYYY-MM-DD HH:mm:ss'));
json
{
  "name": "11.npm",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dayjs": "^1.11.10"
  }
}
yaml
lockfileVersion: '6.0'

settings:
  autoInstallPeers: true
  excludeLinksFromLockfile: false

dependencies:
  dayjs:
    specifier: ^1.11.10
    version: 1.11.10

packages:

  /dayjs@1.11.10:
    resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
    dev: false
bash
 pnpm init # 初始化清单文件 package.json
Wrote to E:\git\code\ajax\黑 AJAX\47.nodejs\11.npm\package.json

{
  "name": "11.npm",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

 pnpm install dayjs # 下载软件包 dayjs
Packages: +1
+
Progress: resolved 1, reused 0, downloaded 1, added 1, done

dependencies:
+ dayjs 1.11.10

Done in 2.7s

 pnpm run start # 运行 index.js

> 11.npm@1.0.0 start E:\git\code\ajax\黑马 AJAX\47.nodejs\11.npm
> node index.js

2024-01-01 07:14:31

小结

  1. npm 软件包管理器作用?

    • 下载软件包以及管理版本
  2. 初始化项目清单文件 package.json 命令?

    • pnpm init
  3. 下载软件包的命令?

    • pnpm i 软件包名字
  4. 下载的包会存放在哪里?

    • 当前项目下的 node_modules 中,并记录在 package.json

npm 安装所有依赖

掌握 npm 安装所有依赖功能

  1. 我们拿到了一个别人编写的项目,但是没有 node_modules,项目能否正确运行?

    • 不能,因为缺少了项目需要的依赖软件包,比如要使用 dayjslodash 但是你项目里没有这个对应的源码,项目会报错的
  2. 为何没有给我 node_modules

    • 因为每个人在自己的本机使用 pnpm 下载,要比磁盘间传递要快(pnpm 有缓存在本机)
  3. 如何得到需要的所有依赖软件包呢?

    • 直接在项目目录下,运行终端命令:npm i 即可安装 package.json 里记录的所有包和对应版本到本项目中的 node_modules

    8e212110-63ad-4073-be36-71b1f6686800

  4. 需求:请在准备好的素材项目中,安装项目所有需要的依赖软件包,并运行 index.js 代码看是否正常!

js
/**
 * 目标:安装所有依赖软件包
 * 场景:一般拿到别人的项目后,只有 package.json 缺少 node_modules 时需要做
 * 语法:在当前项目终端下,输入命令:npm i
 * 效果:会根据 package.json 记录的所有包和版本开始下载
 */

const dayjs = require('dayjs');
console.log(dayjs().format('YYYY-MM-DD HH:mm:ss'));

// 求数组里最大值
const _ = require('lodash');
console.log(_.max([1, 2, 8, 3, 4, 5]));
json
{
  "name": "12.npm-install",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dayjs": "^1.11.10",
    "lodash": "^4.17.21"
  }
}
yaml
lockfileVersion: '6.0'

settings:
  autoInstallPeers: true
  excludeLinksFromLockfile: false

dependencies:
  dayjs:
    specifier: ^1.11.10
    version: 1.11.10
  lodash:
    specifier: ^4.17.21
    version: 4.17.21

packages:

  /dayjs@1.11.10:
    resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
    dev: false

  /lodash@4.17.21:
    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
    dev: false
bash
 pnpm i # 安装所有依赖软件包
Packages: +2
++
Progress: resolved 2, reused 2, downloaded 0, added 2, done

dependencies:
+ dayjs 1.11.10
+ lodash 4.17.21

Done in 520ms

 pnpm start # 运行 index.js

> 12.npm-install@1.0.0 start E:\git\ajax\黑马 AJAX\code\47.nodejs\12.npm-install
> node index.js

2024-01-01 07:15:51
8

小结

  1. 当前项目中只有 package.json 没有 node_modules 怎么办?

    • 当前项目目录下,打开终端,执行 pnpm i 安装所有依赖软件包
  2. 为什么 node_modules 不进行传递?

    • 因为用 npm 下载有缓存在本机,比磁盘之间传递要快

npm 全局软件包 nodemon

掌握本地软件包和全局软件包区别,体验 nodemon 的使用

  1. 软件包区别:

    • 本地软件包:当前项目内使用,封装属性和方法,存在于 node_modules
    • 全局软件包:本机所有项目使用,封装命令和工具,存在于系统设置的位置
  2. nodemon 作用:替代 node 命令,检测代码更改,自动重启程序

  3. 使用:

    • 安装:pnpm i nodemon -g-g 代表安装到全局环境中)
    • 运行:nodemon 待执行的目标 js 文件
  4. 需求:使用 nodemon 命令来启动素材里准备好的项目,然后修改代码保存后,观察终端重启应用程序

js
/**
 * 目标:使用 nodemon 全局软件包,检测文件变化,自动重启程序
 * 语法:
 *  1. 安装 nodemon 全局软件包,命令:npm i nodemon -g
 *  2. 使用 nodemon 来执行目标 js 文件
 * 体验:启动后,修改代码,保存后观察终端效果
 */

const dayjs = require('dayjs');
console.log(dayjs().format('YYYY-MM-DD HH:mm:ss'));

// 求数组里最大值
const _ = require('lodash');
console.log(_.max([1, 2, 8, 3, 4, 5]));

// ❯ nodemon .\index.js
// [nodemon] 3.0.2
// [nodemon] to restart at any time, enter `rs`
// [nodemon] watching path(s): *.*
// [nodemon] watching extensions: js,mjs,cjs,json
// [nodemon] starting `node .\index.js`
// 2024-01-01 07:17:03
// 8
// [nodemon] clean exit - waiting for changes before restart
// [nodemon] restarting due to changes...
// [nodemon] starting `node .\index.js`
// 2024-01-01 07:17:07
// 8
// [nodemon] clean exit - waiting for changes before restart

小结

  1. 本地软件包和全局软件包区别?

    • 本地软件包,作用在当前项目,封装属性和方法
    • 全局软件包,本地所有项目使用,封装命令和工具
  2. nodemon 作用?

    • 替代 node 命令,检测代码更改,自动重启程序
  3. nodemon 怎么用?

    • 先确保安装 pnpm i nodemon -g
    • 使用 nodemon 执行目标 js 文件

Node.js 概念和常用命令总结

把上面学的模块化语法,包的概念,常用命令进行总结

  1. Node.js 模块化:把每个文件当做一个模块,独立作用域,按需加载,使用特定标准语法导出和导入使用

    • CommonJS 标准:一般应用在 Node.js 项目环境中

      导出导入
      语法module.exports = {}require("模块名或路径")
    • ECMAScript 标准:一般应用在前端工程化项目中

      导出导入
      默认export.default {}import 变量名 from '模块名或路径'
      命名export 修饰定义语句import { 同名变量 } from '模块名或路径'
  2. Node.js 包:把模块文件,代码文件,其他资料聚合成一个文件夹就是包

    • 项目包:编写项目需求和业务逻辑的文件夹

    • 软件包:封装工具和方法进行使用的文件夹(一般使用 npm 管理)

      • 本地软件包:作用在当前项目,封装的属性/方法,供项目调用编写业务需求,例如:dayjslodash
      • 全局软件包:作用在所有项目,一般封装的命令/工具,支撑项目运行,例如:nodemon

    c4a62339-21a2-4e2d-9694-e2405a9299b2

  3. Node.js 常用命令:

    功能命令
    执行 js 文件node xxx
    初始化 package.jsonpnpm init
    下载本地软件包pnpm i 软件包名
    下载全局软件包pnpm i 软件包名 -g
    删除软件包pnpm uni 软件包名

小结

  1. 安装本地软件包的命令是什么?

    • pnpm i 软件包名