Node.js学习

Node.js学习

npm

NPM(Node Package Manager)是一个 JavaScript 包管理工具,也是 Node.js 的默认包管理器。

安装包

npm 安装分为本地安装和全局安装:

  • npm install <pkg> :本地安装,安装到当前目录的 node_modules 目录下,可以用 npm root 查看绝对路径;

  • npm install -g <pkg> :全局安装,安装到安装Node.js时用 npm config set prefix 配置的文件夹下,可以用 npm root -g 查看全局包安装路径,用 npm list -g --depth=0 来查看所有全局包:

    PS C:\Users\xxx> npm list -g --depth=0
    D:\Program Files\nodejs\node_global # 全局包安装的位置,下面是当前系统中已经安装的全局包
    +-- @types/node@24.0.3
    +-- @vue/cli-service-global@4.5.19
    +-- @vue/cli@5.0.8
    +-- axios@1.10.0
    +-- ts-node@10.9.2
    +-- typescript@5.8.3
    `-- vue@3.5.22
    

本地包只能当前工程使用,全局包可以整个系统上的工程共享。因此对于提供命令行工具的包应当全局安装,其他包本地安装。

本地安装的包又分为开发依赖(devDependencies)和生产依赖(dependencies)。使用 npm install [--save/-S] 是放在生产依赖(dependencies),如果加上 --save-dev/-D 则是放在开发依赖(devDependencies)。而如果不想作为依赖,只是想下下来临时用下,则可以用 npm install --no-save 不作为依赖。

卸载包

# 卸载本地包
npm uninstall/un <pkg>
# 卸载全局包
npm uninstall/un -g <pkg>

package.json

package.json | npm 中文网

详解package.json和package-lock.json虽然每天都用npm安装包,但是你们对package.js - 掘金

image

使用 npm init 创建package.json。主要字段如下:

{
  // 基本描述
  "name": "my-awesome-project", // 包名
  "version": "1.0.0", // 语义化版本
  "description": "A project to showcase package.json features", // 包描述
  "keywords": [
    "example",
    "package.json",
    "guide"
  ], // npm搜索时的关键字
  "author": "Jane Doe", // 作者
  "license": "MIT", // 开源许可证
  "private": true, // 指示此包为私有,不会被上传到npm仓库
  "repository": { // 代码仓库
    "type": "git",
    "url": "https://github.com/janedoe/my-awesome-project.git"
  },
  "homepage": "https://www.baidu.com", // 项目主页

  // 依赖配置
  "dependencies": { // 生产环境依赖
    "express": "^4.17.1",
    "mongoose": "^5.12.3"
  },
  "devDependencies": { // 开发环境依赖
    "jest": "^26.6.3",
    "eslint": "^7.22.0"
  },
  "peerDependencies": { // 如果当前包是react的插件,则这里的意思是当前包需要的react版本。不会自动安装这里指定的react
    "react": "^16.0.0"
  },
  "optionalDependencies": { // 在找不到这里的包时,仍允许npm继续往下执行
    "fsevents": "^2.0.0"
  },
  "bundledDependencies": [ // 指定需要随当前包打包时一起被打包的包。这里指定的包必须是在dependencies或devDependencies中出现过的
      "@npm/renderized",
      "@npm/super-streams"
  ],
  "engines": { // 指定项目兼容的Node.js或npm版本
    "node": ">=12.0.0",
    "npm": ">=6.0.0"
  },

  // 文件、目录配置
  "main": "index.js", // 包的入口文件(其中会导出js模块,当使用require导入该包时,返回的就是这里的文件中的module.exports属性的值)
  "browser": "index.js", // 如果包只跑在浏览器,而不允许在服务端使用,用这个属性指定入口文件
  
  // 命令、脚本配置 
  "scripts": { // 可以通过npm run <stage>运行的命令。名字随意,如果恰好是npm的生命周期stage的钩子名称,则会随着npm在特定时机自动执行
    "start": "node index.js",
    "build": "webpack --config webpack.config.js",
    "test": "jest",
    "lint": "eslint ."
  },
  
  // 三方工具的配置
  "eslintConfig": {
    "extends": "eslint:recommended",
    "rules": {
      "no-console": "warn"
    }
  },
  "resolutions": { // (yarn专用)
    "lodash": "4.17.21"
  }
}

模块

Node.js诞生时,Javascript尚未支持模块。所以Node.js利用Javascript的特性,通过函数的方式实现了模块,称为CommonJS模块。而ES6新增的模块语法称为ES模块。

CommonJS模块

在这个规范下,每个.js文件都是一个模块,它们内部各自使用的符号都互不冲突,例如,hello.jsmain.js都有全局变量var s = 'xxx',但互不影响。

写一个模块

// hello.js
'use strict';

let g_var = 1.1;

function greet(name) {
    console.log(`greeting ${name}`);
}

// 给module对象的exports成员赋值,表示这些符号要从当前模块导出
module.exports = {
    greet,
    g_var,
}

// 也可以用
exports.greet = greet;
exports.g_var = g_var;
// 但是不可以用
exports = {
	greet,
    g_var
}

使用这个模块

'use strict';

// 引入模块,注意不要文件后缀
const mod1 = require('./hello');

console.log(mod1.g_var)
mod1.greet(s);

注意

  • 默认情况下,Node.js使用的就是 CommonJS 模块(因此node会认为所有 .js 后缀的都是 CommonJS 模块),如果已经在 package.json 中配置了 "type": "module" ,则需要将文件后缀改为 .cjs 才能被认为是 CommonJS 模块。
  • require() 是Node.js提供的内置函数,非Javascript关键字
  • module 是Node.js的内置对象,exports 是其成员,非Javascript关键字
  • node会依次在 内置模块 -> 全局模块 -> 当前模块 下查找 hello.js
  • 引入的模块内容会在 require() 语句处被执行。

Node.js中CommonJS的实现原理

Node.js在调用 require() 函数时,会将其中引入的js文件包装为一个匿名函数,并就地调用:

// const mod1 = require('./hello')的效果简化后大概类似:
const mod1 = (function() {
    // 这里填充hello.js的源码
})();

从而使得原来hello.js中的所有符号都成为mod1的属性,而mod1是引入方定义的不重复名字,自然就避免了符号冲突。

而为了导出模块内符号,Node.js是为每个.js文件都在加载前都预定义了一个module对象:

// 实际上完整的hello.js文件
let module = {
    id: 'hello',
    exports: {}
};
let load = function (exports, module) { // 这里有exports参数是为了方便使用 exports.xxx = xxx 的语法
    // 这里填充hello.js中的源码,其中会为module.exports赋值,从而实现符号的导出
    return module.exports;
}
let exported = load(module.exports, module);
save(module, exported);

然后在导入方使用 require(模块路径) 时,require() 就会查找该文件对应的 module.id ,找到匹配的后就返回对应的 module.exports ,从而就可以使用其中的导出符号了(没赋值给 exports 自然就是未导出符号)。

另外,前面说了还可以直接 exports.xxx = xxx ,但是不允许 exports = {xxx} ,原因也显而易见了:exports = {xxx} 是创建了一个新的对象,赋值给的 exports 由于只是参数的指针拷贝,对其修改无法影响到外部的 module.exports ,自然使得 exports = {xxx} 不会其效果。

ECMAScript模块

ESM模块用 export 关键字导出一个JS对象,用 import 关键字导入一个模块的导出对象。

写一个模块

// hello.mjs
// 'use strict'; // ES6 默认开启严格模式

let g_var1 = 1.1;
export let g_var2 = 2.2;

export function greet1(name) {
  console.log(`greeting1 ${name}`);
}

export function greet2(name) {
  console.log(`greeting2 ${name}`);
}

// 默认导出
// 定义一个临时对象,存储所要导出的所有符号
const hello = {
  g_var1, // g_var1没有用命名导出,但是被默认导出了,所以也是导出符号
  g_var2, // g_var2已经被命名导出过了
  greet: greet1 // greet1默认导出时取了个导出名
};
// 设置这个临时对象位默认导出符号
export default hello;

注意

  • ECMAScript模块的文件名后缀是 .mjs 。或者在 package.json 中配置 "type": "module" 字段来说明使用的当前包默认是 ES Module(因此node会认为所有 .js 后缀的都是 ECMAScript模块)。
  • ES6 默认开启严格模式
  • export 符号定义 是命名导出,导入时需要用 import { 对应的符号名 } from ... 来导入,使用时直接使用对应的符号名;
  • export default 符号集合 是默认导出,导入时需要用 import 默认导出名 from ... 来导入,使用时需要使用 默认导出名.符号名 来访问;
  • 默认导出和命名导出之间不冲突,可以同时写。默认导出的存在意义只是方便导入者不必在 import 中全部写出想要导入的符号。

使用这个模块

import hello2 from './hello.mjs'; // 此时导入了g_var1、g_var2、greet1
import { g_var2, greet1, greet2 } from './mod2/hello.mjs'; // 此时导入了g_var2、greet1、greet2

console.log(hello2.g_var1);
console.log(g_var2);
hello2.greet('meha555'); // 只能使用默认导出名了
greet1('meha555');
greet2('meha555');

注意:

  • 默认导出名 hello2 随便起的,不必和模块定义的.mjs中的 export default xxx 的符号名称相同;
  • 默认导入和命名导入不能混用,如果一个符号是用默认导入来导入的,则他就得按照默认导入的用法 默认导出名.符号名 来使用。命名导入同理。
  • 一旦一个符号被默认导出(尽管可能也命名导出了),则它就成为了默认导出符号,使用时只能按照默认导出符号的导入和使用方式来使用。

基本模块

内置全局对象

与浏览器提供的Web API提供的全局对象 window 类似,Node.js提供的全局对象是 global

process也是Node.js提供的一个对象,它代表当前Node.js进程。

fs

stream

http

crypto

posted @ 2025-10-30 16:42  3的4次方  阅读(0)  评论(0)    收藏  举报