NPM (Node Package Manager),NodeJS 包或模块管理工具,比较新的 NodeJS 版本一般内置 NPM 。NPM 有点类似于 Maven 在 Java 开发中的作用,NPM 项目也和 Maven 项目类似,包含了创建、编译、运行、打包、部署等功能。
ECMAScript 6 (ES6) 是最新的 JavaScript 语言的标准化规范,它的目标是使 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ES6 出现之前 Javascript 模块的加载方案,最主要的是 CommonJS 规范和 AMD 规范。CommonJS 规范应用于服务器,实现同步加载,比如 NodeJS。AMD 规范应用于浏览器,比如 requireJS (异步加载)。同时还有 CMD 规范,比如 seaJS (同步加载)。
1. 系统环境
操作系统:Windows 10 (x64)
NVM:1.1.11
NodeJS: 14.21.3 LTS
NPM:6.14.18
工作目录:D:\workshop\nodejs
2. 创建 npm 项目
进入工作目录 D:\workshop\nodejs,手动创建子目录 npmdemo,在命令行控制台进入该子目录,运行如下命令:
D:\workshop\nodejs\npmdemo> npm init
This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help init` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (npmdemo) version: (1.0.0) description: entry point: (index.js) test command: git repository: keywords: author: license: (ISC) About to write to D:\workshop\nodejs\npmdemo\package.json: { "name": "npmdemo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } Is this OK? (yes)
注:可以运行带 -y 参数的命令 npm init -y,即出现交互选择时都自动选 yes。init 命令运行完成后,npmdemo 目录先生成一个 package.json 文件。
创建 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下:
console.log("NPM demo");
修改 D:\workshop\nodejs\npmdemo\package.json 文件 ,内容如下:
{ "name": "npmdemo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start":"node index.js" }, "author": "", "license": "ISC" }
运行
D:\workshop\nodejs\npmdemo> npm run start
> [email protected] start D:\Workshop\dev-nodejs\npmdemo > node index.js NPM Demo
3. ES6 模块
ES6 的模块设计思想是静态化的,它在编译时确定模块的依赖关系以及输入和输出的变量,也就是说它在编译时就完成了模块加载,我们称为 “编译时加载” 或者静态加载,效率上比 CommonJS 要高。
ES6 新增了 export 和 import 语法(或命令、关键字)来实现模块之间的功能调用(或数据复用),而且逐渐取代 CommonJS/AMD 规范的 exports 和 require 语法,现已成为浏览器和服务器通用的模块解决方案。
示例,在 npmdemo 项目下,创建 common.js 模块使用 export 导出,在 index.js 使用 import 导入 common.js 导出的函数、对象和变量。
创建一个 D:\workshop\nodejs\npmdemo\common.js 文件,内容如下
function func() { console.log("common -> func()") } let obj = { name: "common -> obj" } let str = "common -> str" export { func,obj,str }
修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下
import * as common from "./common.js" console.log("type: " + typeof(common)) common.func() console.log(common.obj.name) console.log(common.str)
运行
D:\workshop\nodejs\npmdemo> node index.js
或
D:\workshop\nodejs\npmdemo> npm run start
(node:22624) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. (Use `node --trace-warnings ...` to show where the warning was created) D:\Workshop\nodejs\npmdemo\index.js:1 import * as common from "./common.js" ^^^^^^ SyntaxError: Cannot use import statement outside a module at wrapSafe (internal/modules/cjs/loader.js:1029:16) at Module._compile (internal/modules/cjs/loader.js:1078:27) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1143:10) at Module.load (internal/modules/cjs/loader.js:979:32) at Function.Module._load (internal/modules/cjs/loader.js:819:12) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:75:12) at internal/main/run_main_module.js:17:47
NodeJS 默认支持 CommonJS 模块规范,export 和 import 属于 ES6 的模块化语法,需要在 package.json 中声明 "type": "module"。修改 package.json,内容如下:
{ ... "author": "", "license": "ISC", "type": "module" }
再次运行
D:\workshop\nodejs\npmdemo> npm run start
type: object common -> func() common -> obj common -> str
4. export
一个模块就是一个独立的文件,该文件内部的资源,外部无法访问,export 用于放开访问限制,将 export 放在变量、函数、类或对象等的前面,允许其它模块访问。
export 规定的是对外接口,必须与模块内部变量/函数/类等建立一一对应的关系。export 输出的接口和其对应的值都是动态绑定的关系,即通过该接口取到的都是模块内部实时的值。
export 必须在模块顶层,可以位于模块顶层的任意位置,但是不能在其它作用域内(比如:函数作用域内)。
1) export 变量
// 格式1,等效于导出对象 {a} export var a = 1 // 格式2 var b = 2 export {b} // 格式3 var c = 3 export {c as d} // 报错,没有模块内部变量 export {4} // 报错,导出格式有错 export 5 // 报错,导出格式有错 var e = 6 export e
2) export 函数
// 格式1 export function f() {} // 格式2 function f() {} export {f} // 报错,导出格式有错 function f() {} export f
3) export default
export default 就是输出一个 default 变量/函数/类,在 import 时可以为它取任意名字。
示例,修改 D:\workshop\nodejs\npmdemo\common.js 文件,内容如下
var s = 'default value' export default s export var v = 99 var obj = {"name": "nodeJS"} export {obj}
修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下
import anyname from "./common.js" console.log("type: " + typeof(anyname)) console.log(anyname)
运行
D:\workshop\nodejs\npmdemo> node index.js
type: string
default value
注:import anyname 默认导入了 common.js 里 export default 导出的变量 s。
5. import
import 用于从其它模块导入变量、函数、对象等资源。import 分为命名式导入(名称导入)和 定义式导入(默认导入)。import 必须放在文件的最开始,且前面不允许有其他逻辑代码,和 Java 、Python 类似。
示例,以上文 export default 部分的 common.js 文件为例
使用 import 命名式导入全部变量,修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下
import * as common from "./common.js" console.log("type: " + typeof(common)) console.log(common.obj.name) console.log(common.v) console.log(common.default)
运行
D:\workshop\nodejs\npmdemo> node index.js
type: object nodeJS 99 default value
使用 import 命名式导入 obj 和 v,修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下
import {obj, v} from "./common.js" console.log(obj.name) console.log(v)
运行
D:\workshop\nodejs\npmdemo> node index.js
nodeJS
99
使用 import 定义式导入 default,修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下
import anyname from "./common.js" console.log("type: " + typeof(anyname)) console.log(anyname)
运行
D:\workshop\nodejs\npmdemo> node index.js
type: string
default value