1+1=10

记记笔记,放松一下...

Node.js 学习笔记(一)

接前面JavaScript基础学习,了解一下Node.js。是什么,如何安装,如何简单使用

Node.js 是什么?

先学习历史:

  • 2009年5月: Ryan Dahl发布了基于V8 JavaScript引擎的Node.js,使JavaScript在服务器端运行成为可能。
  • 2010年: 引入事件循环(Event Loop)作为核心特性,提供更稳定的API和模块。
  • 2011年: 支持Windows平台,并引入npm(Node Package Manager)作为包管理工具。
  • 2012年: Ryan Dahl离开Node.js社区。0.8.0和0.10.0版本推出,增强性能并引入更多特性。
  • 2015年: io.js与Node.js合并,重新统一社区并促进更多发展。Node.js基金会成立。
  • 2019年:Node.js基金会和JS基金会,组合成OpenJS基金会。
  • ...

可是Node.js到底是什么?

说真的,看过几次这段话:

Node.js是一个基于Chrome V8引擎的JavaScript运行环境,用于构建高性能、可伸缩的网络应用。它采用事件驱动、非阻塞I/O模型,使得能够处理大量并发连接。

但看完,还是不知道Node.js是什么东西。可能对.js这个东西误解太深了,压根没去想它是可以和CPython对标的东西:

- Node.js CPython
创建时间 2009年5月 1991年
编程语言 JavaScript Python
并发性和异步编程 非阻塞、事件驱动,适用于处理大量并发连接 通过asyncio支持异步编程
多线程支持 单线程,通过事件循环和异步编程处理并发 多线程编程,受GIL限制,不适用于CPU密集型任务
包管理 npm(Node Package Manager,创建于2010年) pip(Python Package Installer,创建于2003年)
框架和库 Express.js、React.js等 Django、Flask、NumPy、Pandas等
其他 JavaScript前后端都可以使用,全栈开发方便 广泛用于各种领域,特别在数据科学和机器学习

Python通常用于服务器端编程,而JavaScript在Node.js出现之前一直是主要用于客户端(浏览器)脚本的语言。Node.js使得可以将JavaScript用于服务器端开发,让开发者可以使用相同的语言在前端和后端编写代码。

走马观花

快速过一下,找找感觉

安装

Node.js安装和python一样简单,官网下载,双击安装。而后控制台验证

1
2
3
4
$ node -v
v20.10.0
$ npm -v
10.2.3

使用

简单看看怎么用的

REPL

和Python一样,Node.js的交互式操作可以通过 REPL(Read-Eval-Print Loop)进行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ node
Welcome to Node.js v20.10.0.
Type ".help" for more information.
> "1"+3
'13'
> 2**3
8
> Math.sin(Math.PI/2)
1
>

别的先不说,电脑上又多了一个计算器,是真的

Hello world

像python一样,先写个脚本:

1
2
// a.js
console.log("Hello world!")

而后执行

1
2
$ node a.js
Hello World!

和python不太一样,#是python的注释符,但在javascript中不用于注释。

尽管如此,在第一行加入 shebang(也称 hashbang)是可以的:

1
2
3
4
#!/usr/bin/env node

// a.js
console.log("Hello World!");

模块使用

使用 http 模块,开启一个监听3000端口的http server:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// server.mjs

import http from 'http';

const server = http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello, 1+1=10!');
});

const PORT = 3000;

server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});

JavaScript模块学习简单认识了CommonJS和ESM模块区别,以及.mjs、.cjs和.js后缀。为简单起见,完全不去碰CommonJS。

模块安装

安装express

1
npm install express

而后使用其监听3000端口:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// server.mjs
import express from 'express';

const app = express();
const PORT = 3000;

app.get('/', (req, res) => {
  res.send('Hello 1+1=10!');
});

app.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});

package.json

package.json 是 Node.js 项目中的配置文件,用于定义项目的元数据、依赖关系以及一些执行脚本。类比到Python中,可能和setup.py有一点点像似。

package.json简单的例子:

1
2
3
4
5
6
7
8
{
  "name": "your-package",
  "version": "1.0.0",
  "main": "index.mjs",
  "scripts": 
    "start": "node index.mjs"
  }
}
  • main 字段用于指定项目的主要入口文件。当其他人使用这个包作为依赖时,Node.js 会默认加载这个文件。
  • scripts 中的 "start" 的脚本,主要由 nodemon 使用,以便于实现自动重启。

虚拟环境?

有点类似python中的venv虚拟环境,而后pip安装的包在虚拟环境中。如下命令会创建一个package.json文件:

1
npm init -y

而后 npm 安装包会在项目目录下

nodemon

nodemon 是一个用于监视 Node.js 应用程序中文件更改并自动重启应用的工具。它可以在控制台中运行,监听指定的文件,当这些文件发生变化时,nodemon 会自动重启应用,而无需手动重新启动应用。

安装方式:

1
npm install -g nodemon

选项 -g 代表全局安装(global install)

使用:

1
nodemon index.mjs

或直接让其使用package.json中通过 scripts 指定的start脚本:

1
nodemon

注意,Node v16开始,提供了命令行选项 --watch,在一定程序上,可以取代nodemon功能:

1
node --watch index.mjs

Webpack

安装

1
2
npm init -y
npm install webpack webpack-cli --save-dev
  • --save-dev 用于将包添加到项目的开发依赖项(devDependencies)中。这意味着这个包在项目运行时并不是必需的,但在开发、构建或测试阶段是必需的。

例子

准备两个源文件:

  • src/index.mjs
1
2
const message = "Hello 1+1=10";
console.log(message);
  • index.html
1
2
3
4
5
6
<!DOCTYPE html>
<html lang="en">
<body>
  <script type="module" src="dist/main.mjs"></script>
</body>
</html>

准备webpack配置文件webpack.config.mjs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import { fileURLToPath } from 'node:url';
import path from 'node:path';

const dist = fileURLToPath(new URL('.', import.meta.url))+'dist';

export default {
  entry: './src/index.mjs',
  output: {
    filename: 'main.mjs',
    path: dist,
  },
};

path必须用绝对路径,这个在ESM下似乎还不好弄。

运行它

1
npx webpack  --mode production

效果:src/index.mjs 被拷贝到 dist/main.mjs,而后,可以通过 html页面看效果

npx 是 Node.js 包自带的一个工具,它允许你直接运行在 node_modules/.bin 目录下安装的可执行文件。换言之,npx 提供了一种方便的方式来执行本地或远程的命令行工具,而不必手动安装或配置这些工具。

如果不像敲npx,也可以:

1
npm run build

前提是需要package.json中添加

1
2
3
"scripts": {
  "build": "webpack --mode production"
}

疑问?

官方说:

1
Out of the box, webpack won't require you to use a configuration file. However, it will assume the entry point of your project is src/index.js and will output the result in dist/main.js minified and optimized for production.

翻译过来:webpack的配置文件不是必须的,它假定入口点是src/index.js,输出是dist/main.js。

对我们的例子,因为用的.mjs,如果去掉配置文件,会报错...

对于初学JavaScript,又不想用CommonJS,只想用ESM格式的人来说,有点水土不服。

其他操作

网络上资料以CJS为主,用ESM模块的话...

获取当前工作目录

1
2
3
4
import { cwd } from 'node:process';

const currentWorkingDirectory = cwd();
console.log('Current Working Directory:', currentWorkingDirectory);

获取脚本路径

  • import.meta.url 是一个特殊的元数据对象,用于获取当前模块的 URL。
  • fileURLToPath 函数用于将 URL 转换为文件路径。这个东西很关键(URL.pathname不好使)。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import url from 'node:url';

console.log(import.meta.url); // file:///e:/debao-js-tt/mytest.mjs
console.log(new URL(import.meta.url).pathname); // /e:/debao-js-tt/mytest.mjs
console.log(url.fileURLToPath(import.meta.url)); // e:\debao-js-tt\mytest.mjs

import path from 'node:path';

const currentScriptPath = url.fileURLToPath(import.meta.url);
const currentScriptDirectory = path.dirname(currentScriptPath); // e:\debao-js-tt
console.log('Current Script Directory:', currentScriptDirectory);

参考

  • https://nodejs.org/en
  • https://en.wikipedia.org/wiki/Node.js
  • https://nodemon.io/
  • https://webpack.js.org/configuration