NodeJs基础

基于Chrome V8引擎的JavaScript运行时环境,Node.js基于事件驱动、非阻塞式I/O的模型进行操作处理

Posted by page on July 9, 2020

node.js

API文档: http://nodejs.cn/api/

global

global是node.js的全局对象,相当于ECMA Script中的windows,其方法和属性都是全局成员

__filename 返回当前执行js脚本名称

__dirname 返回当前执行js脚本路径(不包含文件名)

process

process.argv

返回一个数组,第0项为node环境文件全路径,第1项为当前模块文件全路径,第2项之后都是访问node.js模块文件时传入的参数

process.cwd()

获取当前工作目录的绝对路径,即运行终端基于的目录

process.env

获取当前执行环境的环境变量(对象),如 NODE_ENV=production node index.js

process.exit([code])

终止 Node.js 进程,便输出 code 自定义状态信息

Buffer

缓存器(处理字节数组)

构造方法(类)

  1. Buffer.alloc/allocUnsafe(size) 初始化/未初始化实例
  2. Buffer.from(string/array/object/buffer,offset/length/encoding) 返回buffer

静态方法

  1. Buffer.isEncoding("utf8") 判断参数编码是否被支持
  2. Buffer.isBuffer(buf) 判是否为Buffer字节序列
  3. Buffer.byteLength(buf) 返回参数Buffer实例的字节长度
  4. Buffer.concat(bufferArray/Array) 拼接多个Buffer实例后返回这个新实例

实例方法

  1. buf.write("page",1,3) 重写Buffer实例的字节序列,默认从索引0字节开始,这里从字节索引为1向后写入3长度个”page”内容
  2. buf.slice(1,3) 自索引1截至索引3之前的Buffer字节序列并返回
  3. buf.toString() 将Buffer实例转为其编码格式的字符串并返回
  4. buf.toJson() 该方法不需显式调用,通过JSON.stringify(buf)自动调用,将Buffer实例转为json串:’type:”Buffer”,data:[…]’

模块

解决问题

命名冲突

文件依赖:各个模块文件之间依赖,新的模块文件需引入太多依赖文件

模块规则

模块定义 1个js文件对应1个模块,模块之间相互独立

模块使用 module.require("url") 当前模块内引入依赖模块 module.exports 被依赖模块文件向外导出内部引用

通过module.exports.fn = ... 来向外导出module的方法/属性; 但是另外提供了快捷方式exports.fn = …,效果同上;

//直接添加至引用导出对象
module.exports.hello = func;
exports.hello = func;(快捷方式)
// 重写了引用导出对象( module.exports),导出无效
exports = {
   sum:sum
};
//但是以下写法重写快捷方式(exports),但未重写module.exports
module.exports = {//批量导出
   sum:sum
};
//重写module.export,此时只能导出1个方法/属性,外部引入后直接使用
module.exports = sum;

模块识别 node.js仅识别js,json,node格式文件为模块,未写后缀名下按此顺序查找

模块缓存 重复引用相同路径的模块不会多次执行模块内部,因为node会缓存已引用模块

模块分类 系统模块,自定义模块,第三方模块

传统库模块化

// 以QRCode(文本转二维码)为例
(function(root, factory) {
  // 判断模块化对象
  if (typeof define === "function" && define.amd) { // 支持AMD
    define("QRCode", factory);
  } else if (typeof module === "object" || typeof exports === "object") { // 支持CommonJS
    module.exports = factory();
    // QRCode = factory();
  } else {  // js全局
    root.QRCode = factory();
  }
})(this,function() {
  // ...qrcode核心方法
})

核心模块

path

路径模块(path字符串处理)

  • .basename(pathStr, ".html") 获取path串最后一部分(文件夹/文件名称),第2参会去掉path串尾部文件后缀并返回
  • .dirname(pathStr) 返回path串的路径部分(除去了最后一部分)
  • .extname(pathStr) 返回path串目标文件扩展名(.文件后缀),文件夹则返回(.文件夹名)
  • .parse(pathStr) 将path串转为指定属性的对象并返回:root,dir,base,ext,name,5个属性
  • .format(obj) 将指定path属性的对象转为path串并返回
  • .isAbsolute("str") 判断是否为根路径,window以冒号识别”C:/”,Linux以反斜杠识别”/foo/index”
  • .join("user/index", "..", "user.html") 将多个路径智能合成1个正确path串,能够自动识别”.”,”..”进行路径跳转
  • .normalize(pathStr) 将path串规范化为正确的路径并返回
  • .relative(pathStr1, pathStr2) 计算出基于参数1路径转至参数2路径的相对路径串并返回
  • .resolve(pathStr1, pathStr2) 基于当前执行路径计算路径参数们后返回绝对路径串,若路径参数带有根路径则基于此根路径
  • .delimiter/.sep() 分别返回当前系统平台的环境变量分隔符(;或:)和路径分界符(/或\);

nodejs的异步I/O(磁盘->内存->磁盘)思想

  1. nodejs异步的执行任务
    • 文件操作
    • 网络操作
  2. nodejs异步事件模型 单线程 + 事件队列
  3. nodejs异步处理 同步处理直接调用方法,接受返回值后进行操作 异步处理通过回调函数进行处理(回调函数参数一般包括error参数和返回值参)

flie

文件模块(文件操作)

  • .stat(filePath,(error,stat) => {}) 获取文件信息 参数1为文件路径; 参数2为回调函数,其中error为错误对象,默认为null;stat为文件对象; stat对象的相关属性:birthtime(文件创建时间),atime(访问时间),mtime(数据被修改时间),ctime(文件状态信息修改时间)

  • .readFile(filePath,encoding,(error,data) => {}) 读取文件内容 data参为文件内容返回值,默认以Buffer字节序列返回; 若指定了encoding参数则将data进行字符编码后返回;

  • .writeFile(filePath,data,encoding,(error) => {}) data为写入数据,可以为字符串,Buffer;encoding默认为”utf8” 该方法写入会覆盖原文件内容,若原文件不存在则创建后写入

  • .existsSync(filePath)

    指定路径是否存在目标文件

  • 流式操作(适用大文件操作)

    • .createReadyStream(path,options..) 事件:data,end… 方法:pipe

    • .createWriteStream(path,options..) 方法:write

      let readstream = file.createReadStream(rpath); //从文件创建流
      let writestream = file.createWriteStream(tpath); //创建写入流
      let num = 0;
      // nodejs基于事件的处理方式提供处理方法
      readstream.on("data",(chunk) => { //监听读取data期间的一波波chunk(流)
        writestream.write(chunk); //处理:一波波流接后写入
        num++;
      });
      readstream.on("end",() => {
        console.log(num);
      });
      //快捷方式
      readstream.pipe(writestream); // 文件流导入
      
    • .mkdir(pathStr,(err) => {}) 创建目录 根据pathStr创建路径的尾部目录

    • .readdir(pathStr,(error,files)=>{}) 读取目录 读取指定路径的目录,并返回一个数组格式的目录项列表files

    • .rmdir(pathStr,(error) => {}) 删除路径尾部目录

http

模块(用于服务端处理http服务)

  • http.createServer((req,res) => {...}) 创建并开启一个http服务类对象并返回,其callback为该对象的request事件的处理

  • server.listen("端口号",IP地址,() => {...}) 设置一个server服务监听的端口,ip地址可选,其callback为监听成功后处理

  • http.incomingMessage类 request对象为该类实例,方法/属性:write,url(仅包含路径部分),method

  • http.ServerResponse类 response对象为该类实例,方法/属性:write,end,writeHead

  • http.request(options,(res) => {...}) 从当前服务器向指定地址发送请求并返回请求一个res对象,options为请求参数对象;

url

模块(处理/解析url串)

  • .parse("url串",boolean) 将url串转为特定属性对象并返回,第2参为是否将query串对象返回 特定对象属性包括protocal,hostname,path,pathname,query,hash,

  • .format(obj) 将具有特定属性与url值的对象转为url串并返回

  • querystring模块(post参数)

  • .parse("查询参数串") 将post参数(具有%格式)转义后返回参数对象,与url.parse(“url串”).query类似 若参数的键存在重复,则生成将值合成为数组

  • .stringfy(obj) 将一个参数对象转为post查询参数串并返回

util

util.inspect(obj, { depth: num })

对象转字符串,可用于良好的打印输出

util.format(format, …args)

格式化字符串

util.callbackify

promise转回调函数

const callbackFoo = util.callbackify(foo);
callbackFoo((err, ret) => {
  if (err) {
    console.log('err', err)
    return
  }
  console.log(ret)
})

其中 foo 为返回 promise 的方法

util.promisify

回调函数转promise

try{
  await util.promisify(fs.readFile)
}catch(e){
  console.log(e)
}

child_process

用于创建子进程也能实现多任务并行处理,也可通过其调用系统的功能指令完成复杂的任务

spawn

启动一个子进程来执行指定的命令,并且可以通过流式数据通信与子进程进行交互;

import ChildProcess from 'child_process'

const { spawn, spawnSync } = ChildProcess
const file = './../fs/index.mjs'
const spawnProcess = spawn('git', ['log', '-1', '--pretty="%ci"', file])
spawnProcess.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`)
  console.log(new Date(data))
})

exec

启动一个 shell,并在 shell 中执行指定命令,执行完毕后插入 stdout/stderr 中,适用于一些命令行工具;

import { exec, execSync } from 'child_process'

const pwd = execSync('pwd')
console.log(pwd.toString())
const ls = execSync('ls -lh')
console.log(ls.toString())

execFile

直接执行某个文件,而无需通过 shell 执行

import { execFile, execFileSync } from 'child_process'

const file = './hello'
const execData = execFileSync(file)
console.log(execData.toString())

fork

衍生新的进程来执行 JavaScript 文件,并且建立一个与子进程的 IPC 通信管道。

import { fork } from 'child_process'

const child = fork('child.mjs') // 使用 fork() 方法创建子进程

child.on('message', (msg) => {
  // 监听来自子进程的消息
  console.log(`Message from child: ${msg}`)
})

child.send('Hello from parent!') // 向子进程发送消息

EventLoop

事件循环

微任务:nextTick、promise

宏任务:setTImeout

I/O队列回调:readFile

setImmediate回调

关闭事件队列回调:process.on(‘exit’)

调试

VS Code调试

VS Code 侧边栏【运行和调试】里,【JavaScript调试终端】按钮新建调试终端,输入运行命令进入调试

或在 package.jsonscripts 配置处【调试】快捷运行调试终端命令,点击进入调试

实现

静态资源托管

let path = require("path");
let fs = require("fs");
let url = require("url");

let mime = require("./mime.json"); //文件类型与mime信息映射

let serverStatic = (request,response,root=".") => {
    let urlObject = url.parse(request.url,true);
    let pathName = urlObject.pathname;
    fs.readFile(root+pathName,"utf8",(error,fileContent) => {
        if(error) response.writeHead(404,{"content-type":"text/plain;charset:utf-8;"});
        let extname = path.extname(pathName);
        let mimeType = "text/html";
        if(mime[extname]){
            mimeType = mime[extname]
        };
        if(mimeType.startsWith("text")){
            mimeType += ";charset:utf-8;"
        }
        response.writeHead(200,{"content-type":mimeType});
        response.end(fileContent);
    })
};
exports.serverStatic = serverStatic;