2、第一个Node.js程序:Hello World程序、Node.js REPL环境、Node.js模块化概念初探、CommonJS与ES Module简介
2.1 从Hello World开始
学任何一门技术,第一个程序都是Hello World。Node.js也不例外。
我个人习惯,先建一个空文件夹,比如叫node-first-app。然后在里面新建一个hello.js文件。写一行代码:
console.log("Hello, Node.js!");
保存后,打开终端,进入这个目录,运行:
node hello.js
你会看到终端输出:
Hello, Node.js!
嗯,就这么简单。但这里有个小细节——console.log在浏览器里是打印到开发者工具的控制台,在Node.js里是打印到终端。我刚开始学的时候,还以为会弹个浏览器窗口出来,结果啥也没有。后来才明白,Node.js是服务端环境,没有DOM,没有窗口。
2.2 Node.js REPL环境
除了写文件运行,Node.js还提供了一个交互式环境,叫REPL。说白了,就是你在终端里直接写代码,它立刻执行给你看。
怎么进REPL?很简单,终端里直接输入node,不带任何文件名:
$ node
>
看到>提示符,就说明你进来了。这时候你可以直接写JavaScript代码:
> 1 + 2
3
> "Hello" + " " + "World"
'Hello World'
> const a = 10
undefined
> a * 2
20
REPL非常适合快速验证一些想法。比如你想试试某个数组方法怎么用,或者某个API返回什么,直接敲进去看看结果就行。
我曾经在调试一个复杂的字符串处理逻辑时,就是靠REPL一步步试出来的。写文件、保存、运行,太慢了。REPL里敲几行,立马看到结果,效率高得多。
退出REPL也很简单,按Ctrl + C两次,或者输入.exit。
2.3 Node.js模块化概念初探
写一个文件很简单,但真实项目不可能把所有代码塞进一个文件里。你想想看,一个大型后端项目,可能有几百个文件。怎么组织?怎么复用?
这就引出了模块化。
模块化,说白了就是把代码拆成一个个独立的小单元,每个单元负责一件事。比如一个文件专门处理用户登录,另一个文件专门处理数据库连接。这样代码清晰,也好维护。
Node.js从诞生起就支持模块化。它默认用的是CommonJS规范。什么意思呢?就是通过require来引入模块,通过module.exports来导出模块。
举个例子。我新建一个math.js:
// math.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = {
add,
subtract
};
然后在app.js里引入它:
// app.js
const math = require('./math.js');
console.log(math.add(5, 3)); // 8
console.log(math.subtract(10, 4)); // 6
运行node app.js,就能看到结果。
这里要注意,require里的路径,./表示当前目录。如果不加./,Node.js会去node_modules里找,那是第三方包的位置。
module.exports暴露出去,外部才能访问。
2.4 CommonJS与ES Module简介
CommonJS是Node.js的“原配”模块系统。它有几个特点:
- 同步加载:
require是同步的,代码会等模块加载完再继续执行。 - 运行时加载:模块在代码执行到
require时才加载。 - 输出的是值的拷贝:模块内部的变化不会影响已经加载的值。
但JavaScript社区后来推出了ES Module(简称ESM),这是ECMAScript官方标准。它的语法更简洁,而且支持静态分析,打包工具可以更好地做tree-shaking(去掉没用到的代码)。
ES Module的写法是这样的:
// math.mjs
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// app.mjs
import { add, subtract } from './math.mjs';
console.log(add(5, 3));
console.log(subtract(10, 4));
注意文件后缀是.mjs,或者你可以在package.json里设置"type": "module",这样.js文件也会被当作ES Module处理。
我建议初学者先掌握CommonJS,因为Node.js生态里大量的包和教程还是基于CommonJS的。等你熟悉了,再慢慢接触ES Module。我自己也是这么过来的,先学会走路,再学跑步。
| 特性 | CommonJS | ES Module |
|---|---|---|
| 语法 | require / module.exports |
import / export |
| 加载方式 | 同步 | 异步 |
| 加载时机 | 运行时 | 编译时(静态) |
| 输出值 | 值的拷贝 | 值的引用 |
| 文件后缀 | .js / .cjs |
.mjs / .js(需配置) |
require is not defined。后来才发现,只要package.json里设置了"type": "module",所有.js文件都会被当作ES Module,这时候就不能用require了。解决办法:要么统一用一种规范,要么把CommonJS文件后缀改成.cjs。
好了,这一章我们写了第一个Node.js程序,体验了REPL的便捷,也初步了解了模块化的概念。下一章,我们会深入Node.js的核心——事件循环和异步编程。准备好了吗?