Node.js相关知识

Node.js 是什么?

  • Node.js 是一个 JavaScript 运行时环境,可以解析和执行 js 代码。
  • 构建于 Chrome 的 V8 引擎之上
  • 没有 BOM、DOM,有 EcmaScript 语法。
  • node 中有很多具名的核心模块
    • fs 文件操作模块
    • http 服务器构建模块
    • path 路径模块
    • os 操作系统信息模块
  • 在核心模块中提供了一些服务器级别的操作 API - 文件读写 - 网络服务的构建 - 网络通信 - http 服务器

node 都有哪些特性?

  • 单线程

    • 不会为每个用户连接创建一个新的线程,仅仅使用一个线程,减少了操作系统的线程创建
      和销毁的时间开销。缺点就是一个用户造成线程的崩溃会导致整个服务的崩溃。
    • 减少内存的开销
  • 事件驱动 event-driven

    • node 中一个时刻只能执行一个事件回调函数,但是执行过程中,可以转而处理
      其他事件,然后返回继续执行原事件的回调函数,这种处理机制,称为“事件环”机制。
  • 非阻塞 I/O

    • I/O 数据传输操作会阻塞代码的执行,极大降低了程序的执行效率,因为一个线程只能处理
      一项任务,要想提高吞吐量必须通过多线程。非阻塞 I/O 机制,可以将异步操作的处理
      代码放在回调函数中,从而提高了程序的执行效率。

浏览器的进程和线程

  • 一个程序可以有多个进程
  • 一个进程可以有多个线程
  • 进程在执行应用程序中拥有独立的内存单元,而多个线程共享内存。
  • 多个线程之间可以相互通信
  • 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口
  • 线程不能独立执行,必须依存在应用程序中

Node.js 适合开发什么?

  • 善于 I/O,不善于计算,因为 Node.js 最擅长的就是任务调度,不适合于利用 CPU 进行过多的运算的程序。
  • 当应用程序需要处理大量并发的 I/O,而在向客户端发出响应之前,应用程序内部不需要
    进行非常复杂处理的时候,Node.js 非常合适。
  • Node.js 也非常适合与 web socket 配合,开发长连接的实时交互应用程序。
    • 用户表单
    • 考试系统
    • 聊天室
    • 图文直播

nodejs 能做什么?

  • Node.js 可以生成动态页面内容
  • Node.js 可以创建,打开,读取,写入,删除和关闭服务器上的文件
  • Node.js 可以收集表单数据
  • Node.js 可以添加,删除,修改数据库中的数据

服务端渲染和客户端渲染的区别

  • 客户端渲染不利于 SEO 搜索引擎优化
  • 服务器渲染可以被爬虫抓取
  • 例如:京东商品列表是服务端渲染,用户评论是客户端渲染(提高用户体验)

小补充:使用 cnpm

  • 方法一 安装 cnpm

    • npm install --global cnpm
  • 方法二 改变 registry

    • npm install jquery --registry=https://registry.npm.taobao.org
  • 方法三 加入配置选项

    • npm config set registry https://registry.npm.taobao.org
    • npm config list

用 nodejs 实现读取文件操作

 const fs = require("fs")

//fs 核心模块提供了一个fs.readFile方法,用来读取指定目录下的文件

//fs.readFile有三个参数
// 1. 读取文件的路径
// 2. 读取文件的编码格式
// 3. 当文件读取完成,调用这个callback回调函数来读取文件的结果

fs.readFile('./data/hello.txt','utf-8',function(err,data){
    if(err){
        console.log(err)    //第一个参数是err对象
        return
    } else {
        console.log(data)   //第二个参数才是data数据
    }
})

用 nodejs 实现写入文件操作

const fs = require('fs')

let msg = 'hello world'

//fs.writeFile有三个参数
// 1. 第一个参数为写入的文件路径
// 2. 第二个参数为写入的内容
// 3. 第三个参数为可选参数,表示写文件的编码格式
// 4. 第四个参数为回调函数,回调函数只有一个参数err,判断是否写入成功。

fs.writeFile('./data/hello.txt',msg,'utf-8',function(err){
    if(err){
        console.log('写入错误' + err)
    } else {
        console.log('ok')
    }
})

nodejs 如何开启进程,进程如何通讯?

  • 进程 process VS 线程 thread

    1. 进程: 进行资源分配和调度的最小单元,有独立内存空间
    2. 线程: 进行运算调度的最小单元,共享进程内存空间
    3. js 是单线程的,但可以多进行执行,如 、WebWorker
  • 为何需要多进程?

    • 多核 CPU,更适合处理多进程
    • 内存较大,多个进程才能更好的利用(单进程有内存上线)
  • 开启进程两种方法 child_process.fork, cluster.fork

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// parent.js
const http = require('http');
const fork = require('child_process').fork;

const server = http.createServer((req, res) => {
if (req.url === '/xxx') {
// 开启子进程

const computeProcess = fork('./compute.js');
computeProcess.send('start');

computeProcess.on('message', (data) => {
res.end('sum is' + data);
});

computeProcess.on('close', () => {
console.log('子进程因报错而退出');
computeProcess.kill();
res.end('error');
});
}
});

server.listen(3000, () => {
console.log('localhost: 3000 starting...');
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// compute.js
function getSum() {
let sum = 0;

for (let i = 0; i < 10000; i++) {
sum += i;
}

return sum;
}

process.on('message', (data) => {
console.log('子进程id', process.pid);
console.log('子进程接收到的数据', data);

const sum = getSum();

process.send(sum);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const http = require('http');
const cpuCoreLength = require('os').cpus().length;
const cluster = require('cluster');

if (cluster.isMaster) {
for (let i = 0; i < cpuCoreLength; i++) {
cluster.fork(); // 开启子进程
}

cluster.on('exit', (worker) => {
console.log('子进程退出');
cluster.fork(); // 进程守护
});
} else {
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('done');
});

server.listen(3000);
}

CommonJS 解析

CommonJS 的核心思想是通过 require 方法来同步加载依赖的其他模块,通过 module.export 来导出需要暴露的接口

require 内部逻辑:
例如:require(X)

  1. 如果 X 是内置模块(比如 require(‘http’))
    a. 返回该模块
    b. 不再继续执行
  2. 如果 X 以 ‘./‘或者 ‘../‘开头
    a. 根据 X 所在父模块,确定 X 的绝对路径
    b. 将 X 当作文件,依次查找以下文件(.x |.x.js | .x.json | .x.node),只要一个存在就返回该文件,不再继续执行
    c. 把 X 当作目录,依次查找以下文件(.x/package.json | .x/index.js | .x/index.json | .x/index.node),只要一个存在就返回并不再执行
  3. 如果 X 不带路径
    a. 根据 X 所在父模块,确定 X 可能的安装目录,依次往上级目录查找,把 X 当成文件名或目录名加载
  4. 找不到抛错’not found’

评论