1.Node.js基础

1.Node.js基础

Node是什么

Node.js不是一门语言,是一个js运行环境。Node.js是一个基于Chrome V8引擎的JavaScript运行环境。Node.js使用了一个事件驱动、非阻塞式I/O的模型,使其轻量又高效。此外Node自带包管理工具npm(node package manager),下载前后端的包可以使用npm指令。

Node.js与js的区别

js:

  • ECMAScript
  • DOM
  • BOM

Node.js:

  • 没有DOM和BOM
  • ECMAScript + es6 的相关语法在node脚本下都能使用
  • 支持commonJS规范

在node环境下如何运行脚本文件

通过node 文件名来运行脚本文件,每次修改js文件需要重新执行才会生效,使用指令npm i -g nodemon安装nodemon,安装后通过nodemon 文件名运行脚本文件,可以监视文件改动,自动重启

REPL指令

  • ctrl + c 退出当前终端
  • ctrl + c 连按两次,退出 Node REPL
  • ctrl + d 退出 Node REPL

Node中的异步编程

Node.js异步编程的直接体现在回调函数上。

异步:就是一个不等待的过程。非阻塞。
优点:性能提高,可以处理大量的并发请求

阻塞代码

readFile

fs.readFile('/etc/passwd', 'utf8', callback);

参数

  1. 文件路径
  2. 编码格式,如果不指定utf-8格式,获取到的data是一个Buffer对象
  3. 回调函数
const fs = require('fs');//同步方法,会产生阻塞效果const data = fs.readFileSync('./runcode.js');//Buffer流console.log(data);//调用Buffer的toString方法console.log(data.toString());//会等读取文件执行完后再执行console.log('阻塞式');

非阻塞代码

异步编程,不阻塞,大大提高程序性能。

const fs = require('fs');//异步编程fs.readFile('./runcode.js', (err, data) => {    //第一个参数为 err ,node中的错误优先机制    if (err) {    //error.stack 属性是一个字符串,描述代码中 Error 被实例化的位置        console.log(err.stack);        return;    }    console.log(data.toString());})//readFile不会阻塞下列代码执行,而readFile需要时间,所以下列代码会先执行console.log('非阻塞式');

setTimeout(()=>{},time)就是典型的非阻塞。阻塞是按顺序执行的,而非阻塞是不需要按顺序执行的。

util.promisify(original)

const fs = require('fs');// 使用promiseconst {    promisify} = require('util');// promisify 将fs.readFile包装成promise对象const readFile = promisify(fs.readFile);async function asyncReadFile() {    try {        //让异步代码同步化        const data = await readFile('./runcode.js');        console.log(data.toString());    } catch (error) {        //打印错误的位置        console.log(error.stack);    }}asyncReadFile();

Generator

const fs = require('fs');const {    promisify} = require('util');const readFile = promisify(fs.readFile);function* read() {    yield readFile('./runcode.js');}//获取Generator对象let gen = read();gen.next().value.then((res) => {    console.log(res.toString());}).catch((err) => {    console.log(err);})

Buffer对象

// 创建一个长度为 10 的 Buffer对象const buf1 = Buffer.alloc(10);console.log(buf1);// 创建一个长度为 10、且用 0x1 填充的 Buffer。 const buf2 = Buffer.alloc(10, 1);console.log(buf2);// 通过数据创建,创建一个包含UTF-8 编码字节const buf3 = Buffer.from('hello world');console.log(buf3);// 创建一个包含字节 [1, 2, 3] 的 Buffer。const buf4 = Buffer.from([1, 2, 3]);console.log(buf4);//写入buf1.write('hello');console.log(buf1);//当填充满后不再填充buf1.write('hello buffer');//读取console.log(buf1.toString()); //hello buff//转换编码格式,默认 utf-8console.log(buf1.toString('base64')); //aGVsbG8gYnVmZg==//合并const buf5 = Buffer.concat([buf1, buf3]);console.log(buf5.toString());//hello buffhello world

此外还有Buffer.compare(buf1, buf2)主要用于 Buffer 实例数组的排序等函数。

Stream流

  • Writable - 可写入数据的流(例如 fs.createWriteStream())
  • Readable - 可读取数据的流(例如 fs.createReadStream())

管道流

管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。

复制文件

const fs = require('fs');//创建可读流const readerStream = fs.createReadStream('./readme.md');//创建可写的流const writerStream = fs.createWriteStream('./test.txt');//通过管道进行传输readerStream.pipe(writerStream);console.log('复制完毕');

链式流

链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作

压缩文件

const fs = require('fs');const zlib = require('zlib');const gzip = zlib.createGzip();fs.createReadStream('./test.txt').pipe(gzip).pipe(fs.createWriteStream('./test.zip'));console.log('文件压缩成功');

解压文件

const fs = require('fs');const zlib = require('zlib');const gunzip = zlib.createGunzip();fs.createReadStream('./test.zip').pipe(gunzip).pipe(fs.createWriteStream('./1.txt'));console.log('文件解压成功');

events

  • 事件循环

    • Node.js 是单进程线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。
    • Node.js 几乎每一个 API 都是支持回调函数的。
    • Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
    • Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.
  • 事件驱动程序

    • Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。
    • 当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户
    • 这个模型非常高效可扩展性非常强,因为 webserver 一直接受请求而不等待任何读写操作。(这也称之为非阻塞式IO或者事件驱动IO)
    • 在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。

整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件。

// 引入 events 模块const events = require('events');// 创建 eventEmitter 对象const eventEmitter = new events.EventEmitter();// 绑定事件处理程序 定好一个主题eventEmitter.on('conn', () => {    console.log('连接成功');    //触发另外的事件处理    eventEmitter.emit('data_receive');});// 绑定事件eventEmitter.on('data_receive', () => {    console.log('数据接收成功');})// 观察者 观察对应的主题要做什么事情eventEmitter.emit('conn');console.log('程序执行完毕');

模块系统

为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。

Node.js提供了exports和require两个对象,其中exports是模块公开的接口,require用于从外部获取一个模块的接口。

创建模块

exports.name = () => {    console.log('Holo');}//也可抛出多个exports.age = () => {    console.log(18);}//还可抛出数组、变量、对象等

引入模块

let Obj = require('./hello');Obj.name()//Obj.age();

抛出构造函数

function Dog() {    let name;    this.setName = function (myName) {        name = myName;    }    this.getName = function () {        console.log('hello' + name);    }}module.exports = Dog;

引入

const Dog = require('./module');const d1 = new Dog();d1.setName('小黄');d1.getName();

如果通过module.exports抛出对象,抛出的就是当前的对象,如果是通过exports抛出,会是一个键值对的形式挂载到对象上{ name: [Function], age: [Function], dog: '小黄' }。并且exports在一个文件中可以有多个抛出,而module.exports只能有一个抛出。

模块加载策略

Node.js 的 require方法中的文件查找策略如下:

由于Node.js中存在4类模块(原生模块和3种文件模块)

  • http、fs、path等,原生模块。
  • ./mod或../mod,相对路径的文件模块。
  • /pathtomodule/mod,绝对路径的文件模块。
  • mod,非原生模块的文件模块

尽管require方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示:

常用内置模块

fs模块

nodejs.jakeyu.top/#%E6%96%87%…

os模块

nodejs.jakeyu.top/#os-%E6%A8%…

url模块

nodejs.cn/api/url.htm…

注意:使用WHATWG标准的新API可以直接const myURL = new URL('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');,不需要再去导入const url = require('url');

path模块

nodejs.jakeyu.top/#path-%E6%A…

  • path.join() 方法会将所有给定的 path 片段连接到一起(使用平台特定的分隔符作为定界符),然后规范化生成的路径。

__dirname: 表示当前执行脚本所在的目录

  • path.resolve() 方法会将路径或路径片段的序列解析为绝对路径。
const path = require('path');//二者都能获取绝对路径名console.log(path.join(__dirname, 'demo1.js'));console.log(path.resolve('demo1.js'));
  • path.extname() 方法会返回 path 的扩展名

注意:如果在服务器端使用path.resolve()方法返回的是服务器上的一个绝对路径,使用path.join()方法返回的是当前电脑上的绝对路径。建议使用path.join(),以免出错。

http模块

搭建服务器

//引入http模块const http = require('http');//创建Server对象const app = http.createServer((req, res) => {    //中文会出现乱码,需要进行设置    res.end('hello node.js');})//监听端口号app.listen(3000);

对不同的url做不同的响应处理

//引入http模块const http = require('http');//引入fs模块const fs = require('fs');//创建Server对象const app = http.createServer((req, res) => {    /*    url: '/about',    method: 'GET',    */    const {        url,        method    } = req;    //对路由进行判断    if (url === '/index' && method === 'GET') {        //返回一个首页给浏览        fs.readFile('./static/index.html', 'utf-8', (err, data) => {            if (err) {                //设置状态码                res.statusCode = 500;                //返回错误信息                res.end('500 - Interval Serval Error!');            }            //成功            res.statusCode = 200;            //设置响应头    表明返回回去的一定是一个html            //一般情况下不会特意去指定,如果需要指定,一定要根据文件类型去指定            //mime 文件类型 https://www.w3school.com.cn/media/media_mimeref.asp            res.setHeader('Content-Type', 'text/html');            //返回信息            res.end(data);        });    } else if (url === '/about' && method === 'GET') {        fs.readFile('./static/about.html', 'utf-8', (err, data) => {            if (err) {                res.statusCode = 500;                res.end('500 - Interval Serval Error!');            }            res.statusCode = 200;            res.setHeader('Content-Type', 'text/html');            res.end(data);        })    } else if (req.headers.accept.indexOf('image/*') !== -1 && method === 'GET') {        //对图片进行处理    注意:这边就是 `image`,匹配的是一个mime类型,而不是你的文件夹名        console.log(url);        //流的方式        fs.createReadStream(__dirname + url).pipe(res);    } else if (url === '/user' && method === 'GET') {        //处理json数据        res.statusCode = 200;        res.setHeader('Content-Type', 'application/json');        res.end(JSON.stringify([{            name: 'Alex'        }]))    } else {        //中文会出现乱码,需要进行设置        res.end('hello node.js');    }})//监听端口号app.listen(3000);

express框架快速上手

  • 在当前项目目录下 npm init 初始化
package name: (node)--- 包名version: (1.0.0) 1.0.1--- 版本description:--- 描述entry point: (Buffer.js) index.js--- 入口文件test command:--- 测试git repository:--- git地址keywords: Alex--- 关键字author: Alex--- 作者license: (ISC)--- 认证

也可以直接npm init --yes直接创建,然后再去修改配置文件。

  • 安装express npm install express --save为了能够安装更快,也可使用淘宝镜像安装 cnpm i express -s

HelloWorld

//引入expressconst express = require('express')//引入fsconst fs = require('fs');//引入pathconst path = require('path');//创建app实例const app = express()const port = 3000//匹配的路由地址app.get('/', (req, res) => res.send('Hello World!'))//index路由app.get('/index', (req, res) => {    fs.readFile('./static/index.html', 'utf-8', (err, data) => {        if (err) {            //设置状态码            res.statusCode = 500;            //返回错误信息            res.end('500 - Interval Serval Error!');        }        //成功        res.statusCode = 200;        //设置响应头    表明返回回去的一定是一个html        res.setHeader('Content-Type', 'text/html');        //返回信息        res.end(data);    });})//匹配所有路由app.get('*', (req, res) => {    res.setHeader('Content-Type', 'image/*');    // console.log(path.resolve(req.url)); // D:\static\images\timg.webp    // console.log(path.join(__dirname, req.url)); // D:\code\MicrosoftVSCodeProjects\Study\node\static\images\timg.webp    fs.readFile(path.join(__dirname, req.url), (err, data) => {        if (err) {            throw err;        }        //end()或send()        res.send(data);    })})//监听端口app.listen(port, () => console.log(`Example app listening on port ${port}!`))

实现Mini的Express框架

MExpress.js

const Application =  require('./application')function MExpress() {  return new Application();}module.exports = MExpress;

application.js

// 需求// 1.实现http服务器// 2.实现get路由请求const http = require('http');const url = require('url');class Application {  constructor() {    this.router = [];  }  get(path, cb) {    this.router.push({      path: path,      method: "GET",      handle: cb    })  }  listen() {    // 创建http服务器    http.createServer((req, res) => {      // 获取前端给我的url路径 pathname      const { pathname } = url.parse(req.url);      for(const route of this.router){        if(route.path === pathname){          route.handle(req,res);          return;        }        // 处理其他的路由        if(route.path === '*'){          route.handle(req,res);          return;        }      }    }).listen(...arguments)  }}module.exports = Application;
免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部