предисловие
Что такое Node.js?
JS — это язык сценариев, и для каждого языка сценариев требуется парсер. Для JS, написанного на HTML-страницах, браузер действует как парсер. Для JS, который должен работать независимо, NodeJS является синтаксическим анализатором.
Синтаксическому анализатору необходимо запустить движок для анализа JavaScript.Node.js использует движок V8, движок JavaScript с открытым исходным кодом от Google.
Таким образом, Node.js — это среда выполнения JavaScript, основанная на движке Chrome V8.
Управляемый событиями механизм Node.js + асинхронный ввод-вывод + высокопроизводительный движок V8 также делают его очень хорошим выбором для написания высокопроизводительных веб-сервисов.
Что может Node.js?
На дворе почти 2020 год, и прошло 10 лет с тех пор, как Node.js был открыт в 2009 году.
После такого длительного периода итерации экосистема Node.js стала очень зрелой, с множеством отличных практик и колес, таких как экспресс, koa и другие фреймворки для веб-разработки.
Node.js, несомненно, также способствовал развитию фронтенд-экологии, такой как фронтенд-инжиниринг.
Говоря о том, что может сделать Node.js, давайте поговорим о том, что я сделал с Node.js.
На работе:
-
Сделал активный инструмент генерации страниц на основе экспресс
-
Сделал платформу системы мониторинга на базе koa+sequelize
-
Я написал несколько автоматизированных скриптов на Node.js, чтобы оптимизировать повторяющуюся работу.
Вне работы:
А вот и null-cli, однострочная команда для повышения вашей эффективности
5 интересных библиотек Node.js, которые перенесут вас в красочный мир Node.js
nodejs + docker + страницы github Настройте свой собственный «Today's Toutiao»
После того, как я наговорил столько глупостей, что мне делать~
Если вы просто хотите недавно понять и изучить Node.js, я надеюсь, что эта статья поможет вам~
Эта статья поможет вам начать работу с Node.js, разобравшись с 13 основными модулями Node.js и практикой TodoList, основанной на родном Node.js!
13 основных основных модулей
1. Модуль событий триггера события
4. Процесс обработки глобальных объектов
6. Модуль URL-адреса унифицированного указателя ресурсов
9. Прочтите модуль readline построчно
10. Модуль строки запроса QueryString
13. DNS-модуль сервера доменных имен
В Node.js гораздо больше встроенных модулей 13. На начальном этапе мы понимаем некоторые общие базовые модули ядра, а затем можем приступить к работе и попрактиковаться~
Если вы не хотите читать всю статью, яблог на гитхабеДля удобства чтения 13 модулей разделены на 13 разделов.Демонстрационный код каждого модуля также можно найти в блоге~
Что реализует TodoList?
Чтобы лучше понять основные модули Node.js, эта демонстрация реализована с собственным API и отделена от некоторых веб-фреймворков и библиотек, таких как Express и koa.
-
Практика RESTful API
-
Статическое сопоставление ресурсов и сжатие gzip
-
Простая реализация внутреннего маршрутизатора маршрутизации
-
Практика основного модуля Node.js
Реализовано простое управление задачами, фронтенд использует vue + element-ui,
TodoList
└───app // 前端代码
│ │ ...
└───controllers // 控制器
│ │ list.js // api 逻辑实现
└───router
│ │ index.js // 注册路由
│ │ router.js // 路由实现
└───utils // 工具类
│ index.js
| data.json // 数据存放
│ index.js // 工程入口
Реализация не зависит от какой-либо библиотеки и не требует установки каких-либо зависимостей.
node index.js
Вы можете запустить сервис, если вы хотите разработать или отладить его самостоятельно, рекомендуется использовать его здесь.nodemon, который реализует горячее обновление и может автоматически перезапускаться.
npm install -g nodemon
nodemon
#or
nodemon index.js
Эффект следующий:
1. Модуль событий триггера события
Node.js используетуправляемый событиями, неблокирующая модель ввода-вывода, что делает ее легкой и эффективной.
Большинство основных API-интерфейсов Node.js используют идиоматическую архитектуру, управляемую событиями, где определенные типы объектов (триггеры) периодически запускают именованные события для вызова объектов-функций (слушателей), так как же Node.js реализует управляемую событиями?
Модуль событий является ядром реализации Node.js, управляемой событиями.Большинство реализаций модуля в узле наследуют класс Events. Например, readstream fs, серверный модуль net.
Модуль событий предоставляет только один объект: events.EventEmitter. Ядром EventEmitter является инкапсуляция функций запуска событий и прослушивания событий.Шаблон наблюдателяреализация.
Все объекты, которые могут инициировать события, являются экземплярами класса EventEmitter. Эти объекты имеют функцию eventEmitter.on(), которая связывает одну или несколько функций с именованными событиями. Имена событий обычно представляют собой строки в верблюжьем регистре, но можно использовать любой допустимый ключ свойства JavaScript.
Объект EventEmitter использует eventEmitter.emit() для инициирования событий.Когда объект EventEmitter инициирует событие, все функции, привязанные к событию, будут вызываться синхронно. Любое значение, возвращаемое вызываемым слушателем, будет проигнорировано и отброшено.
Давайте изучим модуль событий на нескольких простых примерах.
1. Базовый пример
Зарегистрируйте экземпляр приложения, наследуйте класс EventEmitter, прослушивайте события с помощью унаследованной функции eventEmitter.on() и запускайте события с помощью eventEmitter.emit().
const EventEmitter = require('events')
/**
* Expose `Application` class.
* Inherits from `EventEmitter.prototype`.
*/
class Application extends EventEmitter {}
const app = new Application()
// 监听hello事件
app.on('hello', data => {
console.log(data) // hello nodeJs
})
// 触发hello事件
app.emit('hello', 'hello nodeJs')
2. Несколько прослушивателей событий, и это указывает на
Когда несколько прослушивателей событий связаны, прослушиватели событий выполняются в том порядке, в котором они зарегистрированы.
При вызове функции слушателя ключевое слово this будет указывать на экземпляр EventEmitter, к которому привязан слушатель. Также можно использовать стрелочные функции ES6 в качестве слушателей, но ключевое слово this не будет указывать на экземпляр EventEmitter.
const EventEmitter = require('events')
class Person extends EventEmitter {
constructor() {
super()
}
}
const mrNull = new Person()
// 监听play事件
mrNull.on('play', function(data) {
console.log(this)
// Person {
// _events:
// [Object: null prototype] { play: [[Function], [Function]] },
// _eventsCount: 1,
// _maxListeners: undefined
// }
console.log(`play`)
})
// 监听play事件
mrNull.on('play', data => {
console.log(this) // {}
console.log(`play again`)
})
// 触发play事件
mrNull.emit('play', 'hello nodeJs')
3. Синхронный против асинхронного
EventEmitter синхронно вызывает всех слушателей в том порядке, в котором они были зарегистрированы.
const EventEmitter = require('events')
class Person extends EventEmitter {
constructor() {
super()
}
}
const mrNull = new Person()
mrNull.on('play', function(data) {
console.log(data)
})
mrNull.emit('play', 'hello nodeJs')
console.log(`hello MrNull`)
// hello nodeJs
// hello MrNull
Функции-слушатели могут переключаться в асинхронный режим работы с помощью методов setImmediate() и process.nextTick().
const developer = new Person()
developer.on('dev', function(data) {
setImmediate(() => {
console.log(data)
})
})
developer.on('dev', function(data) {
process.nextTick(() => {
console.log(data)
})
})
developer.emit('dev', 'hello nodeJs')
console.log(`hello developer`)
// hello developer
// hello nodeJs
// hello nodeJs
4. Слушатели событий, которые вызываются только один раз
Используйте eventEmitter.once() для регистрации слушателей, которые могут быть вызваны не более одного раза. Когда событие запускается, прослушиватель отменяет регистрацию, а затем вызывается снова.
const EventEmitter = require('events')
class Person extends EventEmitter {
constructor() {
super()
}
}
const mrNull = new Person()
mrNull.once('play', () => {
console.log('play !')
})
mrNull.emit('play')
mrNull.emit('play')
// play ! 只输出一次
5. Последовательность запуска события
Перед регистрацией события запустите событие, оно не будет запущено!!
const EventEmitter = require('events')
class Person extends EventEmitter {
constructor() {
super()
}
}
const mrNull = new Person()
mrNull.emit('play')
mrNull.on('play', () => {
console.log('play !')
})
// 无任何输出
6. Удалите прослушиватель событий
const EventEmitter = require('events')
class Person extends EventEmitter {
constructor() {
super()
}
}
const mrNull = new Person()
function play() {
console.log('play !')
}
mrNull.on('play', play)
mrNull.emit('play')
// mrNull.off("play", play); v10.0.0版本新增,emitter.removeListener() 的别名。
// or
mrNull.removeListener('play', play)
mrNull.emit('play')
// play ! 移除后不再触发
2. Модуль локального пути
Node.js предоставляет модуль пути для обработки путей к файлам и каталогам.Разные операционные системы ведут себя по-разному!
1. Получите имя каталога пути
const path = require('path')
path.dirname('/path/example/index.js') // /path/example
2. Получить расширение пути
const path = require('path')
path.extname('/path/example/index.js') // .js
3. Это абсолютный путь
const path = require('path')
path.isAbsolute('/path/example/index.js') // true
path.isAbsolute('.') // false
4. Сращивание фрагментов пути
path.join('/path', 'example', './index.js') // /path/example/index.js
5. Преобразовать последовательность путей или фрагментов пути в абсолютные пути.
path.resolve('/foo/bar', './baz')
// 返回: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/')
// 返回: '/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif')
// 如果当前工作目录是 /home/myself/node,
// 则返回 '/home/myself/node/wwwroot/static_files/gif/image.gif'
6. Нормализация путей
path.normalize('/path///example/index.js') // /path/example/index.js
7. Разбираем путь
path.parse('/path/example/index.js')
/*
{ root: '/',
dir: '/path/example',
base: 'index.js',
ext: '.js',
name: 'index' }
*/
8. Путь сериализации
path.format({
root: '/',
dir: '/path/example',
base: 'index.js',
ext: '.js',
name: 'index'
}) // /path/example/index.js
9. Получите относительный путь от к до
path.relative('/path/example/index.js', '/path') // ../..
3. Модуль файловой ОС fs
В некоторых сценариях нам необходимо выполнять такие операции, как добавление, удаление, изменение и проверка файлов.Nodejs предоставляет модуль fs, который позволяет нам работать с файлами.
Давайте представим некоторые часто используемые API
1. Прочитайте файл
const fs = require('fs')
const fs = require('fs')
// 异步读取
fs.readFile('./index.txt', 'utf8', (err, data) => {
console.log(data) // Hello Nodejs
})
// 同步读取
const data = fs.readFileSync('./index.txt', 'utf8')
console.log(data) // Hello Nodejs
// 创建读取流
const stream = fs.createReadStream('./index.txt', 'utf8')
// 这里可以看到fs.createReadStream用到了我们前面介绍的events eventEmitter.on() 方法来监听事件
stream.on('data', data => {
console.log(data) // Hello Nodejs
})
2. Запись/изменение файлов
При записи в файл, если файл не существует, он будет создан и записан, а если файл существует, содержимое файла будет перезаписано.
const fs = require('fs')
// 异步写入
fs.writeFile('./write.txt', 'Hello Nodejs', 'utf8', err => {
if (err) throw err
})
// 同步写入
fs.writeFileSync('./writeSync.txt', 'Hello Nodejs')
// 文件流写入
const ws = fs.createWriteStream('./writeStream.txt', 'utf8')
ws.write('Hello Nodejs')
ws.end()
3. Удалить файлы/папки
- Удалить файлы
// 异步删除文件
fs.unlink('./delete.txt', err => {
if (err) throw err
})
// 同步删除文件
fs.unlinkSync('./deleteSync.txt')
- удалить папку
// 异步删除文件夹
fs.rmdir('./rmdir', err => {
if (err) throw err
})
// 同步删除文件夹
fs.rmdirSync('./rmdirSync')
4. Создайте папку
// 异步创建文件夹
fs.mkdir('./mkdir', err => {
if (err) throw err
})
// 同步创建文件夹
fs.mkdirSync('./mkdirSync')
5. Переименуйте файлы/папки
const fs = require('fs')
// 异步重命名文件
fs.rename('./rename.txt', './rename-r.txt', err => {
if (err) throw err
})
// 同步重命名文件夹
fs.renameSync('./renameSync', './renameSync-r')
6. Скопируйте файлы/папки
const fs = require('fs')
// 异步复制文件
fs.copyFile('./copy.txt', './copy-c.txt', (err, copyFiles) => {
if (err) throw err
})
// 同步复制文件夹
fs.copyFileSync('./null', 'null-c')
7. Статус папки — файл/папка
const fs = require('fs')
// 异步获取文件状态
fs.stat('./dir', (err, stats) => {
if (err) throw err
// 是否是文件类型
console.log(stats.isFile()) // false
// 是否是文件夹类型
console.log(stats.isDirectory()) // true
})
// 同步获取文件状态
const stats = fs.statSync('./stats.txt')
// 是否是文件类型
console.log(stats.isFile()) // true
// 是否是文件夹类型
console.log(stats.isDirectory()) // false
В некоторых сложных сценариях работы модуль fs должен выполнять много оценок и обработки, здесь я рекомендую всем использоватьfs-extra, который расширяет некоторые методы на основе fs, чтобы упростить некоторые сложные операции!
4. Процесс обработки глобальных объектов
Объект процесса — это глобальный объект, который вы можете использовать где угодно без необходимости. process является экземпляром EventEmitter, поэтому в процессе также есть связанные прослушиватели событий. Используя объект процесса, вы можете легко обрабатывать операции, связанные с процессом.
общие атрибуты процесса
Аргументы командной строки процесса: process.argv
process.argv — это группа параметров текущего процесса выполнения, первый параметр — узел, второй параметр — имя исполняемого в данный момент файла .js, а ниже — список параметров, установленных во время выполнения.
node index.js --tips="hello nodejs"
/*
[ '/usr/local/bin/node',
'xxx/process/index.js',
'--tips=hello nodejs' ]
*/
Массив аргументов командной строки для Node: process.execArgv
Свойство process.execArgv возвращает массив аргументов командной строки Node.
node --harmony index.js --version
console.log(process.execArgv); // [ '--harmony' ]
console.log(process.argv);
/*
[ '/usr/local/bin/node',
'xxx/process/index.js',
'--version' ]
*/
Версия, когда узел был скомпилирован: process.version
Свойство process.version возвращает номер версии Node при его компиляции.Номер версии хранится во встроенной переменной Node NODE_VERSION.
console.log(process.version) // v10.15.3
PID process.pid текущего процесса
Свойство process.pid возвращает PID текущего процесса.
console.log('process PID: %d', process.pid)
//process PID: 10086
общий метод обработки
текущий рабочий каталог process.cwd()
Метод process.cwd() возвращает текущий рабочий каталог процесса.
console.log(process.cwd()) // /Users/null/nodejs/process
Завершить текущий процесс: process.exit([code])
Метод process.exit() завершает текущий процесс.Этот метод может принимать необязательный код параметра статуса выхода.Если он не передан, он вернет код состояния 0, указывающий на успех.
process.on('exit', function(code) {
console.log('进程退出码是:%d', code) // 进程退出码是:886
})
process.exit(886)
микрозадача nodejs: process.nextTick()
Метод process.nextTick() используется для задержки выполнения функции обратного вызова.Метод nextTick задерживает выполнение функции обратного вызова в обратном вызове до следующего цикла цикла обработки событий.По сравнению с setTimeout(fn, 0), метод nextTick намного эффективнее Этот метод Наша функция обратного вызова может быть вызвана перед любым вводом-выводом.
console.log('start')
process.nextTick(() => {
console.log('nextTick cb')
})
console.log('end')
// start
// end
// nextTick cb
обрабатывать стандартный объект потока
В процессе есть три операции по подготовке потоков.В отличие от других потоковых операций, потоковые операции в процессе синхронно записываются и блокируются.
Стандартный поток ошибок: process.stderr
process.stderr — это поток с возможностью записи, указывающий на стандартный поток ошибок. console.error реализуется через process.stderr.
Стандартный входной поток: process.stdin
process.stdin — это Readable Stream, указывающий на стандартный входной поток.
process.stdin.setEncoding('utf8')
process.stdin.on('readable', () => {
let chunk
// 使用循环确保我们读取所有的可用数据。
while ((chunk = process.stdin.read()) !== null) {
if (chunk === '\n') {
process.stdin.emit('end')
return
}
process.stdout.write(`收到数据: ${chunk}`)
}
})
process.stdin.on('end', () => {
process.stdout.write('结束监听')
})
Стандартный поток вывода: process.stdout
process.stdout — это поток с возможностью записи, указывающий на стандартный поток вывода. console.log реализован через process.stdout
console.log = function(d) {
process.stdout.write(d + '\n')
}
console.log('Hello Nodejs') // Hello Nodejs
5. http-модуль
Модуль http — очень важный основной модуль в Node.js. С помощью модуля http вы можете создать http-сервер, используя его метод http.createServer, и вы можете создать http-клиент, используя его метод http.request. (В этой статье об этом не говорится) Инкапсуляция протокола HTTP и связанных с ним API в Node относительно низкоуровневая. Он может обрабатывать только потоки и сообщения. Для обработки сообщений он разбирает только заголовки и тела сообщений, но не анализировать фактические сообщения.Заголовок и содержимое тела. Это не только решает первоначальную сложность HTTP, но и поддерживает больше HTTP-приложений.
Объект http.IncomingMessage
Объект IncomingMessage создается http.Server или http.ClientRequest и передается в качестве первого параметра событию запроса http.Server и событию ответа http.ClientRequest соответственно.
Его также можно использовать для доступа к статусу, заголовкам и данным ответа и т. д. Объект IncomingMessage реализует интерфейс Readable Stream, и в объекте есть некоторые события, методы и свойства.
Немного отличается в http.Server или http.ClientRequest .
http.createServer([requestListener]) создает HTTP-сервер
Чтобы реализовать функцию HTTP-сервера, создайте объект сервера http.Server с помощью метода http.createServer.
Этот метод принимает необязательный входящий параметр requestListener, который представляет собой функцию, которая будет использоваться в качестве прослушивателя событий запроса для http.Server. Если он не передается, его необходимо добавить отдельно в событии запроса объекта http.Server.
var http = require('http')
// 创建server对象,并添加request事件监听器
var server = http.createServer(function(req, res) {
res.writeHeader(200, { 'Content-Type': 'text/plain' })
res.end('Hello Nodejs')
})
// 创建server对象,通过server对象的request事件添加事件事件监听器
var server = new http.Server()
server.on('request', function(req, res) {
res.writeHeader(200, { 'Content-Type': 'text/plain' })
res.end('Hello Nodejs')
})
http.Server серверный объект
Объект http.Server — это эмиттер событий EventEmitter, который генерирует события: request, connection, close, checkContinue, connect, upgrade, clientError.
Функция прослушивания событий запроса — это функция (запрос, ответ) { }, которая имеет два параметра: запрос — это экземпляр http.IncomingMessage, а ответ — экземпляр http.ServerResponse.
В объекте http.Server также есть несколько методов: после вызова server.listen http.Server может принимать входящие соединения от клиентов.
http.ServerResponse
Объект http.ServerResponse используется для обработки клиентских запросов в ответ.
http.ServerResponse — это объект, созданный внутри HTTP-сервера (http.Server) и переданный в качестве второго параметра функции слушателя события «запрос».
http.ServerResponse реализует интерфейс Writable Stream, и его ответ клиенту, по сути, является операцией над этим доступным для записи потоком. Это также EventEmitter, включая события закрытия, завершения.
Создайте http.сервер
Создать http.Server Используйте метод http.createServer().Для обработки клиентских запросов сервер должен прослушивать событие «запрос» от клиента.
В функции обратного вызова события «запрос» будут возвращены экземпляр http.IncomingMessage и экземпляр http.ServerResponse.
const http = require('http')
/**
* @param {Object} req 是一个http.IncomingMessag实例
* @param {Object} res 是一个http.ServerResponse实例
*/
const server = http.createServer((req, res) => {
console.log(req.headers)
res.end(`Hello Nodejs`)
})
server.listen(3000)
Экземпляр http.ServerResponse является доступным для записи потоком, поэтому можно передать файловый поток в поток ответа res. В следующем примере показано, как передать изображение в ответ HTTP:
const http = require('http')
/**
* @param {Object} req 是一个http.IncomingMessag实例
* @param {Object} res 是一个http.ServerResponse实例
*/
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'image/jpg' })
const r = require('fs').createReadStream('./kobe.jpg')
r.pipe(res)
})
server.listen(3000)
6. Модуль URL-адреса унифицированного указателя ресурсов
Node.js предоставляет модуль url для обработки и анализа URL-адресов.
1. Какими свойствами обладает объект URL?
const { URL } = require("url");
const myURL = new URL("https://github.com/webfansplz#hello");
console.log(myURL);
{
href: 'https://github.com/webfansplz#hello', // 序列化的 URL
origin: 'https://github.com', // 序列化的 URL 的 origin
protocol: 'https:', // URL 的协议
username: '', // URL 的用户名
password: '', // URL 的密码
host: 'github.com', // URL 的主机
hostname: 'github.com', // URL 的主机名
port: '', // URL 的端口
pathname: '/webfansplz', // URL 的路径
search: '', // URL 的序列化查询参数
searchParams: URLSearchParams {}, // URL 查询参数的 URLSearchParams 对象
hash: '#hello' // URL 的片段
}
Свойства объекта URL доступны для записи, кроме origin и searchParams, которые доступны только для чтения.
2. Сериализировать URL-адрес
const { URL } = require('url')
const myURL = new URL('https://github.com/webfansplz#hello')
console.log(myURL.href) // https://github.com/webfansplz#hello
console.log(myURL.toString()) // https://github.com/webfansplz#hello
console.log(myURL.toJSON()) // https://github.com/webfansplz#hello
7. Сожмите модуль zlib
В процессе потоковой передачи, чтобы уменьшить объем передаваемых данных и ускорить передачу, поток часто сжимается.
Так обстоит дело с HTTP-стримингом: для повышения скорости отклика веб-сайта он будет сжат на стороне сервера, а клиент распаковывает данные после получения данных.
Модуль Zlib в Node.js обеспечивает функции сжатия и распаковки потоков. Модуль Zlib обеспечивает привязки к классам Gzip/Gunzip, Deflate/Inflate, DeflateRaw/InflateRaw, которые могут реализовывать потоки, доступные для чтения/записи.Сжатие и распаковка.
О gzip и deflate
deflate (RFC1951) — это алгоритм сжатия, закодированный с использованием LZ77 и Хаффмана. gzip (RFC1952) — это формат сжатия, представляющий собой простую инкапсуляцию deflate, gzip = заголовок gzip (10 байтов) + фактический контент, закодированный с помощью deflate + хвост gzip (8 байтов). При передаче HTTP обычно используется алгоритм сжатия gzip.Потоки данных HTTP, сжатые с помощью gzip, будут идентифицированы Content-Encoding: gzip в заголовке HTTP.
Accept-Encoding в заголовке HTTP-запроса отправляется браузером на сервер, объявляя тип декомпрессии, поддерживаемый браузером.
Accept-Encoding: gzip, deflate, br
Content-Encoding в заголовке ответа HTTP — это сервер, сообщающий браузеру, какой тип сжатия используется.
Content-Encoding: gzip
Студенты, знакомые с оптимизацией веб-производительности, я полагаю, что они знакомы с gzip, мы будем использовать gzip, чтобы понять модуль zlib.
1. Сжатие/распаковка файлов
сжатие файлов
const zlib = require('zlib')
const fs = require('fs')
const gzip = zlib.createGzip()
const inp = fs.createReadStream('zlib.txt')
const out = fs.createWriteStream('zlib.txt.gz')
inp.pipe(gzip).pipe(out)
распаковка файла
const zlib = require('zlib')
const fs = require('fs')
const gunzip = zlib.createGunzip()
const inp = fs.createReadStream('./un-zlib.txt.gz')
const out = fs.createWriteStream('un-zlib.txt')
inp.pipe(gunzip).pipe(out)
2. Сжатие gzip на стороне сервера
const fs = require('fs')
const http = require('http')
const zlib = require('zlib')
const filepath = './index.html'
const server = http.createServer((req, res) => {
const acceptEncoding = req.headers['accept-encoding']
if (acceptEncoding.includes('gzip')) {
const gzip = zlib.createGzip()
res.writeHead(200, {
'Content-Encoding': 'gzip'
})
fs.createReadStream(filepath)
.pipe(gzip)
.pipe(res)
} else {
fs.createReadStream(filepath).pipe(res)
}
})
server.listen(4396)
8. Модуль потокового вещания
Stream — это абстрактный интерфейс для обработки потоковых данных в Node.js. Модуль потока используется для создания объектов, реализующих интерфейс потока.
Node.js предоставляет множество объектов потока. Например, запросы HTTP-сервера и process.stdout являются экземплярами потоков.
Потоки могут быть доступны для чтения, записи или чтения и записи. Все потоки являются экземплярами EventEmitter.
Хотя важно понимать, как работают потоки, модуль потока в основном используется разработчиками для создания новых типов экземпляров потока. Для разработчиков, которые в основном используют потоковые объекты, редко требуется использовать модуль потока напрямую.
тип потока
В Node.js существует четыре основных типа потоков:
-
Writable — поток, в который можно записывать данные (например, fs.createWriteStream()).
-
Readable — поток, из которого данные могут быть прочитаны (например, fs.createReadStream()).
-
Duplex — доступный для чтения и записи поток (например, net.Socket).
-
Transform — дуплексный поток, который может изменять или преобразовывать данные во время чтения и записи (например, zlib.createDeflate()).
API для потребления потоков
const http = require('http')
const server = http.createServer((req, res) => {
// req 是一个 http.IncomingMessage 实例,它是可读流。
// res 是一个 http.ServerResponse 实例,它是可写流。
let body = ''
// 接收数据为 utf8 字符串,
// 如果没有设置字符编码,则会接收到 Buffer 对象。
req.setEncoding('utf8')
// 如果添加了监听器,则可读流会触发 'data' 事件。
req.on('data', chunk => {
body += chunk
})
// 'end' 事件表明整个请求体已被接收。
req.on('end', () => {
try {
const data = JSON.parse(body)
// 响应信息给用户。
res.write(typeof data)
res.end()
} catch (er) {
// json 解析失败。
res.statusCode = 400
return res.end(`错误: ${er.message}`)
}
})
})
server.listen(1337)
// curl localhost:1337 -d "{}"
// object
// curl localhost:1337 -d "\"foo\""
// string
// curl localhost:1337 -d "not json"
// 错误: Unexpected token o in JSON at position 1
Доступный для чтения поток использует API EventEmitter для уведомления приложения, когда данные могут быть прочитаны из потока (например, событие req data в примере). Существует множество способов чтения данных из потока.
Доступные для записи потоки (такие как res в примере) предоставляют такие методы, как write() и end() для записи данных в поток.
И записываемые, и читаемые потоки используют EventEmitter API несколькими способами для передачи текущего состояния потока. Дуплексные потоки и потоки преобразования доступны как для записи, так и для чтения.
Приложениям, которым нужно только записывать данные или потреблять данные из потока, не нужно напрямую реализовывать интерфейс потока и обычно не нужно вызывать require('stream').
Для большинства разработчиков nodejs модуль потока обычно не используется напрямую, но особенно важно понимать механизм работы потокового потока.
9. Прочтите модуль readline построчно
Модуль readline — это модуль построчного чтения для потоковой передачи содержимого со ссылкой на модуль через require('readline'). Вы можете использовать модуль readline для чтения стандартного ввода, для чтения файлового потока построчно и для некоторого взаимодействия с пользователем на консоли.
const readline = require('readline')
const rl = readline.createInterface({
// 监听的可读流
input: process.stdin,
// 逐行读取(Readline)数据要写入的可写流
output: process.stdout
})
rl.question('你如何看待 null-cli ?', answer => {
console.log(`感谢您的宝贵意见:${answer}`)
rl.close()
})
Многие интересные инструменты CLI основаны на readline, и заинтересованные студенты также могут попробовать~
10. Модуль строки запроса QueryString
Модуль строки запроса — это один из модулей инструментов в Node.js для обработки строк запроса в URL-адресах, т. е. части строки запроса. Строка запроса относится к: в строке URL, начиная со знака вопроса "?" (исключая?) до точки привязки "#" или до конца строки URL (если # существует, он заканчивается #, если он не существует, он идет в конец строки URL) называется строкой запроса. Модуль querystring анализирует строки запроса URL-адреса в объекты или сериализует объекты в строки запроса.
1. Сериализация объекта в строку запроса
querystring.stringify(obj[, sep][, eq][, options])
const querystring = require('querystring')
const obj = {
url: 'github.com/webfansplz',
name: 'null'
}
console.log(querystring.stringify(obj)) // url=github.com%2Fwebfansplz&name=null
2. Строка запроса анализируется в объект
const querystring = require('querystring')
const o = querystring.parse(`url=github.com%2Fwebfansplz&name=null`)
console.log(o.url) // github.com/webfansplz
3. Кодировать параметры в строке запроса
Метод querystring.escape кодирует строку запроса, которую можно использовать при использовании метода querystring.stringify.
const str = querystring.escape(`url=github.com%2Fwebfansplz&name=null`)
console.log(str) // url%3Dgithub.com%252Fwebfansplz%26name%3Dnull
4. Декодировать параметры в строке запроса
Метод querystring.unescape является обратным по отношению к querystring.escape и может использоваться при использовании метода querystring.parse.
const str = querystring.escape(`url=github.com%2Fwebfansplz&name=null`)
console.log(querystring.parse(str)) // { 'url=github.com%2Fwebfansplz&name=null': '' } ✖️
console.log(querystring.parse(querystring.unescape(str))) // { url: 'github.com/webfansplz', name: 'null' }
11. модуль модуль
Node.js реализует простую систему загрузки модулей. В Node.js существует однозначное соответствие между файлами и модулями, что можно понимать как файл — это модуль. Реализация его модульной системы в основном зависит от модуля глобального объекта, который реализует такие механизмы, как exports (экспорт) и require() (загрузка).
1. Загрузка модуля
Файл в Node.js — это модуль. Например, загрузите circle.js в тот же каталог, что и в index.js:
// circle.js
const PI = Math.PI
exports.area = r => PI * r * r
exports.circumference = r => 2 * PI * r
// index.js
const circle = require('./circle.js')
console.log(`半径为 4 的圆面积为 ${circle.area(4)}`) // 半径为 4 的圆面积为 50.26548245743669
В Circle.js два метода area() и окружность экспортируются через экспорт, и эти два метода можно вызывать из других модулей.
экспорт и модуль.экспорт
exports — это простая ссылка на module.exports. Если вам нужно экспортировать модуль как функцию (например, конструктор) или вы хотите экспортировать полный объект экспорта вместо экспорта как свойства, вам следует использовать module.exports.
// square.js
module.exports = width => {
return {
area: () => width * width
}
}
// index.js
const square = require('./square.js')
const mySquare = square(2)
console.log(`The area of my square is ${mySquare.area()}`) // The area of my square is 4
2. Доступ к основному модулю
Когда Node.js запускает файл напрямую, для свойства require.main устанавливается сам модуль. Таким образом, вы можете использовать это свойство, чтобы определить, запускается ли модуль напрямую:
require.main === module
Например, для index.js в приведенном выше примере значение над узлом index.js равно true, но при передаче require('./index') значение равно false.
модуль предоставляет атрибут имени файла, значение которого обычно равно __filename. Поэтому точку входа текущей программы можно получить через require.main.filename.
console.log(require.main.filename === __filename) // true
3. Разберите путь к модулю
Используя функцию require.resolve(), вы можете получить точное имя файла модуля, загруженного с помощью require.Эта операция возвращает только разрешенное имя файла, и модуль не будет загружен.
console.log(require.resolve('./square.js')) // /Users/null/meet-nodejs/module/square.js
Рабочий процесс require.resolve:
require(X) from module at path Y
1. If X is a core module,
a. return the core module
b. STOP
2. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.json is a file, parse X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP
LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
a. Parse X/package.json, and look for "main" field.
b. let M = X + (json main field)
c. LOAD_AS_FILE(M)
2. If X/index.js is a file, load X/index.js as JavaScript text. STOP
3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
4. If X/index.node is a file, load X/index.node as binary addon. STOP
LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
c. DIR = path join(PARTS[0 .. I] + "node_modules")
b. DIRS = DIRS + DIR
c. let I = I - 1
5. return DIRS
4. Кэш модуля
Модули кэшируются в объекте require.cache после первой загрузки, и удаление пар ключ-значение из этого объекта приведет к тому, что следующее требование перезагрузит удаленный модуль.
Многократные вызовы require('index') не обязательно приводят к многократному выполнению кода в модуле. Это важная функция, с помощью которой можно возвращать частично завершенные объекты; таким образом, транзитивные зависимости могут быть загружены, даже если они могут привести к циклическим зависимостям.
Если вы хотите, чтобы модуль выполнялся несколько раз, вы должны вывести функцию, а затем вызвать эту функцию.
Рекомендации по кэшированию модулей
Модули кэшируются на основе разрешенного имени файла. В зависимости от того, где сделан вызов, он может разрешиться в другой файл (например, если его нужно загрузить из папки node_modules). Таким образом, при синтаксическом анализе других файлов нет гарантии, что require('index') всегда будет возвращать один и тот же объект.
Кроме того, в файловой системе или системах, нечувствительных к регистру, разные имена файлов могут разрешаться в один и тот же файл, но кеш все равно будет рассматривать их как разные модули, загружая файл несколько раз. Например: require('./index') и require('./INDEX') вернут два разных объекта, независимо от того, являются ли './index' и './INDEX' одним и тем же файлом.
5. Циклические зависимости
Когда функция require() вызывается в цикле, модуль может не выполняться при возврате.
// a.js
console.log('a starting')
exports.done = false
const b = require('./b.js')
console.log('in a, b.done = %j', b.done)
exports.done = true
console.log('a done')
// b.js
console.log('b starting')
exports.done = false
const a = require('./a.js')
console.log('in b, a.done = %j', a.done)
exports.done = true
console.log('b done')
// main.js
console.log('main starting')
const a = require('./a.js')
const b = require('./b.js')
console.log('in main, a.done=%j, b.done=%j', a.done, b.done)
Сначала main.js загрузит a.js, затем a.js загрузит b.js. В этот момент b.js снова попытается загрузить a.js.
Чтобы предотвратить бесконечные циклы, a.js возвращает незавершенную копию в b.js. Затем b.js прекращает загрузку и возвращает свой объект экспорта в модуль a.js.
Таким образом main.js завершает загрузку двух файлов a.js и b.js. Результат выглядит следующим образом:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
6. Файловый модуль
При загрузке файлового модуля, если поиск по имени файла не найден. Затем Node.js попытается добавить расширения .js и .json и снова попытается найти. Если он по-прежнему не найден, добавляется расширение .node и попытка поиска повторяется.
Для файлов .js он будет проанализирован как текстовый файл JavaScript, .json будет проанализирован как файл JOSN, .node попытается преобразовать в скомпилированный файл плагина, который будет загружен dlopen.
разрешение пути
Когда загруженный файловый модуль использует префикс '/', это означает абсолютный путь. Например, require('/home/null/index.js') загрузит файл /home/null/index.js.
При использовании префикса './' это означает относительный путь. Например, когда на index.js ссылается require('./circle'), круг.js должен находиться в том же каталоге для успешной загрузки.
Если нет префикса '/' или './', модуль, на который делается ссылка, должен быть "модулем ядра" или модулем в node_modules.
Если загруженный модуль не существует, функция require() выдаст ошибку с атрибутом кода «MODULE_NOT_FOUND».
7. __dirname
Имя каталога текущего модуля. То же, что path.dirname() для __filename.
console.log(__dirname) // /Users/null/meet-nodejs/module
console.log(require('path').dirname(__filename)) // /Users/null/meet-nodejs/module
console.log(__dirname === require('path').dirname(__filename)) // true
8. объект модуля
module представляет собой ссылку на текущий модуль внутри каждого модуля. А на module.exports можно ссылаться через глобальный экспорт объектов. модуль не является глобальным объектом, а больше похож на внутренний объект модуля.
module.children
Все объекты модуля, импортированные этим модулем
module.exports
module.exports создается через модульную систему. Иногда это не работает так, как мы хотим, а иногда мы хотим, чтобы модуль был экземпляром некоторого класса. Таким образом, назначение экспортируемого объекта в module.exports, но экспорт требуемого объекта приведет к назначению связывающей локальной переменной экспорта, что может быть не тем результатом, который нам нужен.
// a.js
const EventEmitter = require('events')
module.exports = new EventEmitter()
// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(() => {
module.exports.emit('ready')
}, 1000)
const a = require('./a')
a.on('ready', () => {
console.log('module a is ready')
})
Обратите внимание, что экспортируемое значение, присвоенное module.exports, должно быть доступно немедленно, оно не будет работать должным образом при использовании обратных вызовов.
экспортирует псевдоним
exports можно использовать как ссылку на module.exports. Как и любой переменной, если вы присвоите ей новое значение, ее старое значение будет недействительным:
function require(...) {
// ...
((module, exports) => {
// Your module code here
exports = some_func; // re-assigns exports, exports is no longer
// a shortcut, and nothing is exported.
module.exports = some_func; // makes your module export 0
})(module, module.exports);
return module;
}
-
module.filename - полное имя файла модуля после парсинга
-
module.id — идентификатор, используемый для различения модуля, обычно полностью разрешенное имя файла.
-
module.loaded - был ли загружен модуль
-
module.parent - родительский модуль, т.е. модуль, который импортировал этот модуль
-
module.require(id)
-
module.require предоставляет функциональность, подобную require(), для загрузки модуля из исходного модуля.
12. Буфер Буферный модуль
До появления TypedArray в языке JavaScript не было механизма для чтения или управления потоками двоичных данных. Класс Buffer был представлен как часть Node.js API для взаимодействия с потоками октетов в потоках TCP, операциях файловой системы и других контекстах.
создать буфер
console.log(Buffer.from([1, 2, 3, 4, 5])) // <Buffer 01 02 03 04 05>
console.log(Buffer.from(new ArrayBuffer(8))) // <Buffer 00 00 00 00 00 00 00 00>
console.log(Buffer.from('Hello world')) // <Buffer 48 65 6c 6c 6f 20 77 6f 72 6c 64>
Буфер и кодировка символов
Когда строковые данные сохраняются в экземпляре Buffer или извлекаются из него, можно указать кодировку символов.
// 缓冲区转换为 UTF-8 格式的字符串
const buffer = Buffer.from('Hello world')
console.log(buffer.toString()) // Hello world
// 缓冲区数据转换为base64格式字符串
const buffer = Buffer.from('Hello world')
console.log(buffer.toString('base64')) // SGVsbG8gd29ybGQ=
// 将base64编码的字符串,转换为UTF-8编码
const buffer = Buffer.from('Hello world')
const base64Str = buffer.toString('base64')
const buf = Buffer.from(base64Str, 'base64')
console.log(buf.toString('utf8')) // Hello world
13. DNS-модуль сервера доменных имен
DNS (система доменных имен, система доменных имен), протокол DNS работает поверх протокола UDP, используя порт номер 53. DNS — это распределенная база данных, которая сопоставляет доменные имена и IP-адреса друг с другом в Интернете, что позволяет пользователям более удобно выходить в Интернет без необходимости запоминать строки IP, которые могут быть напрямую прочитаны машинами. Проще говоря, это преобразование доменного имени (URL) в соответствующий IP-адрес. Модуль dns в Node.js предоставляет функции разрешения DNS. При использовании метода net.connect(80, 'github.com/webfansplz') в модуле dns или метода http.get({ host: 'github.com/webfansplz' }) в модуле http DNS используется в the hood Метод dns.lookup в модуле выполняет разрешение доменного имени.
Два метода разрешения доменных имен модуля DNS
1. Используйте базовую службу DNS операционной системы для разрешения
При использовании базовой службы DNS операционной системы для разрешения доменных имен вам не нужно подключаться к сети и использовать только собственную функцию разрешения DNS системы. Эта функция реализована методом dns.lookup().
dns.lookup(hostname[ options], callback): разрешает доменное имя (например, «www.baidu.com») до первой найденной записи A (IPv4) или записи AAAA (IPv6)
hostname указывает доменное имя, которое необходимо разрешить.
options может быть объектом или целым числом. Если параметр options не указан, подходят как IP-адреса v4, так и v6. Если options является целым числом, оно должно быть равно 4 или 6. Если options является объектом, он будет содержать следующие два необязательных параметра:
-
семейство: опционально, IP-версия. Если предусмотрено, должно быть 4 или 6. Если они не указаны, допустимы как IP-адреса v4, так и v6.
-
подсказки: по желанию. Если указано, может быть одним или несколькими флагами getaddrinfo. Если не указан, никакие флаги не будут переданы в getaddrinfo.
callback Функция обратного вызова, параметры включают (ошибка, адрес, семья). При ошибке параметр err является объектом Error. Параметр адреса представляет IP-адрес v4 или v6. Параметр семейства равен 4 или 6, что указывает на версию протокола адреса.
const dns = require('dns')
dns.lookup(`www.github.com`, (err, address, family) => {
if (err) throw err
console.log('地址: %j 地址族: IPv%s', address, family) // 地址: "13.229.188.59" 地址族: IPv4
})
2. Подключитесь к DNS-серверу, чтобы разрешить доменное имя.
В модуле dns, кроме метода dns.lookup(), DNS-сервер используется для разрешения доменного имени, для чего требуется подключение к сети.
dns.resolve(hostname[ rrtype], callback): разрешает доменное имя (например, «www.baidu.com») в массив типа, указанного rrtype
hostname указывает доменное имя, которое необходимо разрешить.
rrtype имеет следующие доступные значения:
rrtype | записи содержат | тип результата | метод быстрого доступа |
---|---|---|---|
'A' | IPv4-адрес (по умолчанию) | string | dns.resolve4() |
'AAAA' | IPv6-адрес | string | dns.resolve6() |
'ANY' | любая запись | Object | dns.resolveAny() |
'CNAME' | запись канонического имени | string | dns.resolveCname() |
'MX' | записи обмена почтой | Object | dns.resolveMx() |
'NAPTR' | запись указателя разрешения имени | Object | dns.resolveNaptr() |
'NS' | записи серверов имен | string | dns.resolveNs() |
'PTR' | запись указателя | string | dns.resolvePtr() |
'SOA' | Начать запись авторизации | Object | dns.resolveSoa() |
'SRV' | послужной список | Object | dns.resolveSrv() |
'TXT' | текстовая запись | string[] | dns.resolveTxt() |
callback Функция обратного вызова, параметры содержат (ошибка, адреса). При ошибке параметр err является объектом Error. Возвращаемое значение адресов зависит от типа записи.
const dns = require('dns')
dns.resolve('www.baidu.com', 'A', (err, addresses) => {
if (err) throw err
console.log(`IP地址 : ${JSON.stringify(addresses)}`) // IP地址 : ["163.177.151.110","163.177.151.109"]
})
// or
dns.resolve4('www.baidu.com', (err, addresses) => {
if (err) throw err
console.log(`IP地址 : ${JSON.stringify(addresses)}`) // IP地址 : ["163.177.151.110","163.177.151.109"]
})
обратный поиск DNS
Разрешите адрес IPv4 или IPv6 в массив имен хостов.
Используйте метод getnameinfo для преобразования входящих адресов и портов в доменные имена и службы.
dns.reverse(ip, callback)
ip представляет собой IP-адрес, который необходимо разрешить в обратном порядке.
callback Функция обратного вызова, параметры включают (ошибка, домены). При ошибке параметр err является объектом Error. domains Массив разрешенных доменных имен.
dns.reverse('8.8.8.8', (err, domains) => {
if (err) throw err
console.log(domains) // [ 'dns.google' ]
})
dns.lookupService(address, port, callback)
address представляет строку IP-адреса для разрешения.
port представляет номер порта для разрешения.
callback Функция обратного вызова, параметры включают (ошибка, имя хоста, сервис). При ошибке параметр err является объектом Error.
dns.lookupService('127.0.0.1', 80, function(err, hostname, service) {
if (err) throw err
console.log('主机名:%s,服务类型:%s', hostname, service) // 主机名:localhost,服务类型:http
})
Ссылаться на
постскриптум
Если вам нравится передняя часть так же, как и мне, и вы любите подбрасывать руками, пожалуйста, следуйте за мной и поиграйте со мной~ ❤️
блог
Нет публики
передний момент