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,没有窗口。

小提示:文件名不一定非要叫hello.js,你可以叫index.js、app.js都行。但别用中文名,容易出编码问题。

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

注意:REPL里定义的变量,退出后就没了。它只是个临时环境,别指望保存什么重要数据。

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里找,那是第三方包的位置。

核心概念:每个文件在Node.js中都是一个独立的模块。模块内部的变量、函数默认是私有的,只有通过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(需配置)
避坑指南:我曾经在一个项目里混用了CommonJS和ES Module,结果报错说require is not defined。后来才发现,只要package.json里设置了"type": "module",所有.js文件都会被当作ES Module,这时候就不能用require了。解决办法:要么统一用一种规范,要么把CommonJS文件后缀改成.cjs

好了,这一章我们写了第一个Node.js程序,体验了REPL的便捷,也初步了解了模块化的概念。下一章,我们会深入Node.js的核心——事件循环和异步编程。准备好了吗?