Loading...
墨滴

城南旧事

2021/08/27  阅读:49  主题:默认主题

Node.js 基础入门

一、Node.js 简介

Node.js 是一个基于 Chrome V8 引擎 的 JavaScript 运行时环境

安装与运行

下载

https://nodejs.org/zh-cn/download/
下载
下载

创建项目

mkdir node
cd node
npm init -y

新建 index.js 文件

const { readFile } = require('fs')

readFile('./package.json', { encoding'utf-8' }, (err, data) => {
    if (err) {
        throw err;
    }
    console.log(data)
})

输入 package.json 文件

node index.js

版本管理

在同一个设备上如何快速切换Node.js 版本?

版本管理工具:

  • n: 一个npm 全局的开源包,是依赖npm 来全局安装、使用的
  • fnm: 快速简单,兼容性支持.node-version和.nvmrc文件
  • nvm: 独立的软件包,Node Version Manager

特点

特点
特点
异步 I/O

当 Node.js 执行 I/O 操作时,会在响应返回并恢复操作,而不是阻塞线程并浪费CPU 循环等待

异步 I/O
异步 I/O
单线程

Node.js 保持了 JavaScript 在浏览器中单线程的特点

优点:

  • 不用处处在意状态同步问题,不会发生死锁
  • 没有线程上下文切换带来的性能开销

缺点:

  • 无法利用多核 CPU
  • 错误会引起整个应用退出,健壮性不足
  • 大量计算占用导致CPU,无法继续执行

浏览器为例,浏览器是多进程,JS 引擎单线程

Browser 进程:浏览器主进程,只有一个

插件进程:插件使用时才创建

GPU 进程:最多一个用于3D 绘制

渲染进程:页面渲染、JS执行、事件处理

  • GUI 渲染线程+
  • JS 引擎线程+ V8
  • 事件触发线程
  • 定时器触发线程
  • 异步请求
跨平台

兼容Windows 和*nix 平台,主要得益于在操作系统与Node 上层模块系统之间构建了一层平台架构

跨平台
跨平台

应用场景

Node.js 在大部分领域都占有一席之地,尤其是I/O密集型的

  • Web 应用:Express / Koa
  • 前端构建: WebpackGUI
  • 客户端软件: VSCode / 网易云音乐
  • 其它:实时通讯、爬虫、CLI 等

二、模块化机制

  1. 何为模块化? 根据功能或业务将一个大程序拆分成互相依赖的小文件,再用简单的方式拼装起来
  2. 为什么模块化? 无模块化问题所有script 标签必须保证顺序正确,否则会依赖报错 全局变量存在命名冲突,占用内存无法被回收 IIFE/namespace 会导致代码可读性低等诸多问题

CommonJS规范

Node.js 支持 CommonJS 模块规范,采用同步机制加载模块

// greeting.js
const prefix = 'hello';
const sayHi = function ({
    return prefix + 'world';
}
module.exports = {
    sayHi
}

// index.js
const { sayHi } = require('./greeting');
sayHi();

exports = module.exports = { }

// greeting.js
const prefix = 'hello';
const sayHi = function ({
    return `${prefix}  world`;
}
exports.sayHi = sayHi;

// index.js
const { sayHi } = require('./greeting');
sayHi();

CommonJS 中 exports、require、module、__filename、__dirname 变量

function (exports, require, module, __filename, __dirname{
    const m = 1;
    module.exports.m = m;
}

加载方式:

  1. 加载内置模块require('fs')
  2. 加载相对 | | 绝对路径的文件模块 require('/User/.../file.js') require('./file.js')
  3. 加载 npm 包 require('lodash')

npm 包查找原则:

require('lodash')

  1. 当前目录node_modules
  2. 如果没有,父级目录的node_modules
  3. 如果没有,沿着路径向上递归,直到根目录下node_modules
  4. 找到之后会加载package.json main 指向的文件,如果没有package.json 则依次查找index.js、index.json、index.node

require.cache

require.cache 中缓存着加载过的模块,缓存的原因:同步加载

  1. 文件模块查找耗时,如果每次require 都需要重新遍历查找,性能会比较差;
  2. 在实际开发中,模块可能包含副作用代码
// 有缓存
const mod1 = require('./foo');
const mod2 = require('./foo');
console.log(mod1 === mod2);     // true

//无缓存
function requireUncached(module{
    delete require.cache[require.resolve(module)];
    return require(module);
}
const mod3 = requireUncached('./foo');
console.log(mod1 === mod3);     // false

其他模块化规范

  • AMD 是 RequireJS 在推广过程中规范化产出,异步加载,推崇依赖前置
  • CMD 是 SeaJS 在推广过程中规范化产出,异步加载,推崇就近依赖
  • UMD (Universal Module Definition) 规范,兼容 AMD 和 CommonJS 模式
  • ES Modules (ESM),语言层面的模块化规范,与环境无关,可借助 babel 编译

常用模块介绍

文件

var fs = require("fs"// 引入 fs 模块
fs.readFile(filename, [options], callback); //读取文件
fs.writeFile(filename, data, [options], callback); // 写文件
fs.appendFile(filename, data, [options], callback); //以追加的方式写文件
fs.open(filename, flags, [mode], callback); //打开文件
fs.mkdir(path, [mode], callback); //创建目录:
fs.readdir(path, callback); //读取目录
fs.exists(path, callback); //查看文件与目录是否存在

Path 模块

var path = require("path"// 引入 path 模块
path.basename(path[, ext]); //返回path的最后-部分
path.dirname(path); // 返回path的目录名
path.normalize(path);//路径解析,得到规范路径
path.isAbsolute(path); //判断路径是否是绝对路径
path.relative(form,to); //方法根据当前工作目录返回从from 到to的相对路径
path.resolve([...paths]); //将路径或路径片段的序列解析为绝对路径

OS 模块

var os = require("os"//引入 os 模块
os.cpus(); //返回每个逻辑cpu内核信息
os.hostname(); //返回操作系统的主机名
os.platform(); //返回标识操作系统平台的字符串
os.userInfo([options]); //返回关于当前有效用户的信息

三、包管理机制

npm介绍

NPM 是Node.js 中的包管理器,提供了安装、删除等其它命令来管理包

常用命令:

  • npm init
  • npm config
  • npm run [cmd]
  • npm install [pkg]
  • npm uninstall [pkg]
  • npm update [pkg]
  • npm info [pkg]
  • npm publish

package.json 文件

  • name 包名称
  • version 版本号
  • main 入口文件
  • scripts 执行脚本
  • dependencies 线上依赖
  • devDependencies 开发依赖
  • repository 代码托管地址

更多 package.json 配置

https://docs.npmjs.com/cli/v7/configuring-npm/package-json

依赖

  • dependencies 业务依赖,应用发布后正常执行所需要的包
  • devDependencies 开发依赖,只用于开发环境
  • peerDependencies 同等依赖,比如一个webpack 插件依赖特定版本的webpack
  • bundledDependencies 打包依赖(npm run pack),必须已经在devDep 或者dep声明过
  • optionalDependencies 可选依赖

私有 npm

  • 镜像公司内部私有 npm
  • 镜像设置npm config set registry=https://bnpm.byted.org

其它

  • 并行安装
  • 扁平管理
  • 锁文件(lockfile)
  • 缓存优化
  • ...
  • npm7 | yarn => lock/扁平/缓存...
  • pnpm => monorepo/硬、符号链接/安全性高...

四、异步编程

Callback

目的:读取 package.json 中main 字段对应的文件内容 问题:如何解决回调地狱?

const fs = require('fs')

fs.readFile('./package.json', { encoding'utf-8' }, (err, data) => {
    if (err) throw err;
    const { main } = JSON.parse(data);
    fs.readFile(main, { encoding'utf-8' }, (err, data) => {
        if (err) throw err;
        console.log(data)
    })
})

Promise

Promise 是一个具有四个状态的有限状态机,其中三个核心状态为 Pending(挂起),Fulfilled(完成)、Rejected(拒绝),以及还有一个未开始状态。

异步
异步

使用 Promise , 实现读取 package.json 中 main 字段对应的文件内容

const { readFile } = require('fs/promises')

readFile('./package.json', { encoding'utf-8' }).then(res => {
    return JSON.parse(res);
}).then(data => {
    return readFile(data.main, { encoding'utf-8' });
}).then(res => {
    console.log(res);
})

如何将 Callback 转为 Promise 形式?ugl.promisify

function promisify(fn, receiver{
    return (...args) => {
        return new Promise((resolve, reject) => {
            fn.apply(receiver, [...args, (err, res) => {
                return err ? reject(err) : resolve(res);
            }]);
        });
    };
}
const readFilePromise = promisify(fs.readFile, fs);

await

await 函数使用 try catch 捕获异常(注意并行处理)

const { readFile } = require('fs/promises')

async () => {
    const { main } = JSON.parse(await readFile('./package.json', { encoding'utf-8' }));
    const data = await readFile(main, { encoding'utf-8' });
    console.log(data);
}

Event

发布订阅模式,Node.js 内置events 模块

比如 HTTP server on('request') 事件监听

//发布订阅模式
const EventEmitter = require('events');

class MyEmitter extends EventEmitter { }
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
    console.log('an event occurred!');
});
myEmitter.emit('event');

// server 监听request 事件
const http = require('http');
const server = http.createServer((req, res) => {
 res.end('hello')
});
server.on('request', (req, res) => {
    console.log(req.url);
});
server.listen(3000);

五、Web 应用开发

HTTP 模块

搭建一个最简单的HTTP 服务?Node.js 内置HTTP 模块

const http = require('http');

http.createServer((req, res) => {
    res.end('hello World\n');
}).listen(3000, () => {
    console.log('App running at http://127.0.0.1:3000/')
})

Koa 介绍

Koa —— 基于 Node.js 平台的下一代Web 开发框架 Koa 它仅仅提供了一个轻量优雅的函数库,使得编写Web 应用变得得心应手, 不在内核方法中绑定任何中间件

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
    ctx.body = 'Hello World';
});

app.listen(3000, () => {
    console.log('App running at http://127.0.0.1:3000/')
});

执行过程

  • 服务启动
    • 实例化 application
    • 注册中间件
    • 创建服务、监听端口
  • 接受/处理请求
    • 获取请求req、res 对象
    • req -> request、res -> response 封装
    • request & response -> context
    • 执行中间件
    • 输出设置到ctx.body 上的内容

Koa 中间件

Koa 应用程序是一个包含一组中间件函数的对象,它是按照洋葱模型组织和执行的

Koa
Koa

常用中间件

  • koa-router:路由解析
  • koa-body: request body 解析
  • koa-logger:日志记录
  • koa-views: 模板渲染
  • koa2-cors :跨域处理
  • koa-session:session 处理
  • koa-helmet:安全防护
  • ...

Koa 中间件繁多,质量参差不齐,需要合理选择,高效组合...

基于Koa 的前端框架

开源:ThinkJS / Egg ...

内部:Turbo、Era、Gulu ...

它们做了什么?

  • Koa 对象 response / request / context / application 等扩展
  • Koa 常用中间件库
  • 公司内部服务支持
  • 进程管理
  • 脚手架

城南旧事

2021/08/27  阅读:49  主题:默认主题

作者介绍

城南旧事