>  기사  >  웹 프론트엔드  >  Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

青灯夜游
青灯夜游앞으로
2021-06-01 11:06:181977검색

이 기사에서는 Nodejs의 모듈화 및 이벤트 루프를 안내합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

5.20은 브라우저에서 Node.js를 실행할 수 있는 온라인 Ide를 출시했습니다 —WebContainers

1 Node.js 소개

Node.js란 정확히 무엇인가요? 제가 배우기 시작했을 때 일부 프런트엔드 지식 영역에 대해 많이 접하지 못했습니다(물론 지금도 여전히 그렇습니다). Node.js에 대한 제 인상은 그 구문이 Javascript와 거의 동일하다는 것입니다. 백엔드를 작성하세요. 당시 자바스크립트를 배운다는 것 = 모든 것을 아는 것 같아 남몰래 행복했던 기억이 납니다! 자, 본론으로 들어가겠습니다

[추천 학습: "nodejs tutorial"]

과거에는 Javascript가 브라우저에서 실행되었습니다. Javascript는 결국 컴퓨터에서 직접 읽을 수 없는 고급 언어입니다. 바이너리 컴퓨터의 세계는 010101 밖에 없습니다... 이때 브라우저의 JavaScript 엔진은 JavaScript가 컴퓨터에 수행하려는 작업을 단계별로 번역하는 번역기 역할을 합니다. 또한 지금은 확실하지 않다고 말합니다.)

Node.js는 Chrome 브라우저의 V8 엔진을 기반으로 하며 Javascript를 효율적으로 컴파일할 수 있으므로 Node.js는 브라우저 외에 또 다른 Javascript 실행 환경이라고 할 수 있습니다.

Tencent Cloud의 클라우드 기능에서 WeChat 공개 계정에 대한 간단한 자동 답장을 사용했던 기억이 납니다. 그때 Node.js 덕분에 프런트엔드 코드의 모듈화에 대한 약간의 경험이 있었습니다!

2 첫 경험

server.js 파일은 다음과 같습니다

// 引入 http 模块
var http = require("http");

//  用 http 模块创建服务
 //req 获取 url 信息 (request)
 //res 浏览器返回响应信息 (response)
http.createServer(function (req, res) {
  // 设置 HTTP 头部,状态码是 200,文件类型是 html,字符集是 utf8
  //Content-Type字段用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,不写就可能会出现乱码哦
  res.writeHead(200, {
    "Content-Type": "text/html;charset=UTF-8"
  });

  // 往页面打印值
  res.write('小林别闹');

  // 结束响应
  res.end();

}).listen(3000); // 监听3000端口

Node 설치 후 터미널에서 node server.js를 실행하세요. 브라우저를 열고 http를 입력하세요. 주소 표시줄: //localhost:3000/ 인쇄된 페이지를 볼 수 있습니다: node server.js 打开浏览器,在地址栏输入http://localhost:3000/ 就能看到页面打印出来:

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

此时我们在本地搭建起一个最简单的服务器,浏览器作为客户端进行访问

2.1 模块化

在上边的代码中,我们注意到了有 var http = require("http"); 这样的语句,作用是引入 http 模块. 在 Node 中,模块分为两类:一是 Node 提供的模块,称为核心模块;二是用户编写的模块,称为文件模块.http 就是核心模块之一,例如使用 http 模块可以创建服务,path 模块处理文件路径,url 模块用于处理与解析 URL.fs模块用于对系统文件及目录进行读写操作等.

2.1.1CommonJS

提到模块化,就必须提一嘴 CommonJS,Node.js 就采用了部分 CommonJS 语法,可以理解为 CommonJS 是一种模块化的标准.在早期为了解决通过script标签引入js文件代码产生的依赖顺序易出错,顶层作用域导致的变量污染等问题

在这里可以梳理一下导出 module.exportsexports 的差别

test2.js 如下:

let str = require('./test1');
console.log(str)

当 test1.js如下:

let str1 = '小林别闹1'
let str2 = '小林别闹2'

exports.str1 = str1
exports.str2 = str2
console.log(module.exports === exports)

在终端执行 node test2.js 结果如下:

/*输出
{ str1: '小林别闹1', str2: '小林别闹2' }
true
*/

//改变test1.js文件变量暴露的方式
/*
exports.str1 = str1
module.exports = str2
console.log(module.exports === exports)
输出:
false
小林别闹2
*/

/*
exports.str1 = str1
module.exports.str2 = str2
console.log(module.exports === exports)
控制台输出:
true
{ str1: '小林别闹1', str2: '小林别闹2' }
*/

可以进行一下总结:

在 Node 执行一个文件时,会给这个文件内生成一个 exports 对象和一个 module 对象,而这个module 对象又有一个属性叫做 exports ,exports 是对 module.exports 的引用,它们指向同一块地址,但是最终导出的是 module.exports,第二次测试 module.exports = str2 改变了地址,所以 str1 没有导出.

另外注意,使用 exports 导出是导出一个对象

2.1.2 Es Module

Javascript 也是在不断的发展进步,这不,Es6版本就加入了Es Module模块

导出:

export const str1 = '小林别闹1'
export const str2 = '小林别闹2'
export default {
    fn() {},
    msg: "小林别闹"
}

导入:

import { st1,str2,obj } from './test.js'

注意 import,直接 node

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

이번에는 가장 간단한 서버를 로컬에 설정했고, 브라우저는 클라이언트 역할을 해서 접속

2.1 모듈화

위 코드에서 http를 도입하는 var http = require("http");와 같은 문이 있음을 확인했습니다. code> 모듈. Node에서 모듈은 두 가지 범주로 나뉩니다. 하나는 핵심 모듈이라고 하는 Node에서 제공하는 모듈이고, 다른 하나는 http라고 하는 사용자가 작성한 모듈입니다. code>는 핵심 모듈 중 하나입니다. 1. 예를 들어 <code>http 모듈을 사용하여 서비스를 만들고, path 모듈은 파일 경로를 처리하고, url code> 모듈은 URL을 처리하고 구문 분석하는 데 사용됩니다.<code>fs 모듈은 시스템 파일 및 디렉터리 등을 읽고 쓰는 데 사용됩니다.

2.1.1 CommonJS

모듈화에 관해서는 CommonJS를 언급해야 합니다. Node.js는 CommonJS 구문의 일부를 채택했는데, 이는 CommonJS가 초기에는 모듈화 표준으로 이해될 수 있습니다. script 태그를 통해 js 파일 코드 순서가 오류가 발생하기 쉽고 최상위 범위로 인한 변수 오염과 같은 문제가 발생합니다여기서 차이점을 정리할 수 있습니다 module.exports 내보내기와 exports

🎜test2.js 사이는 다음과 같습니다.🎜
console.log(&#39;1&#39;)
setTimeout(() => {
  console.log(&#39;2&#39;)
})
console.log(&#39;3&#39;)//输出132
🎜test1.js가 다음과 같은 경우:🎜
console.log(&#39;start&#39;)
setTimeout(() => {//定时器1
  console.log(&#39;timer1&#39;)
  setTimeout(function timeout () {//定时器2
    console.log(&#39;timeout&#39;);
  },0);
  setImmediate(function immediate () {//定时器3
    console.log(&#39;immediate&#39;);
  });
  Promise.resolve().then(function() {
    console.log(&#39;promise1&#39;)
  })
}, 0)
setTimeout(() => {//定时器4
  console.log(&#39;timer2&#39;)
  Promise.resolve().then(function() {
    console.log(&#39;promise2&#39;)
  })
}, 0)
Promise.resolve().then(function() {
  console.log(&#39;promise3&#39;)
})
console.log(&#39;end&#39;)
🎜 node test2를 실행합니다. 🎜<pre class="brush:js;toolbar:false;">console.log(1) setTimeout(()=&gt;{console.log(2)},1000)//宏任务1 async function fn(){ console.log(3) setTimeout(()=&gt;{console.log(4)},20)//宏任务2 //return Promise.reject()返回失败状态的,不会输出6,弄不清楚为啥 return Promise.resolve() } async function run(){ console.log(5) await fn() //console.log(6), } run() //需要执行150ms左右 for(let i=0;i&lt;90000000;i++){} setTimeout(()=&gt;{//宏任务3 console.log(7) new Promise(resolve=&gt;{ console.log(8) resolve() }).then(()=&gt;{console.log(9)}) },0) console.log(10) // 1 5 3 10 4 7 8 9 2</pre>🎜요약을 만들 수 있습니다:🎜🎜Node에서 파일을 실행할 때 이 파일의 내용은 <code>내보내기 생성 객체와 module 객체가 있고 이 module 객체에는 exports라는 속성이 있습니다. exports는 module.exports
. 동일한 주소를 가리키지만 최종 내보내기는 module.exports입니다. 두 번째 테스트에서는 module.exports = str2가 변경되었습니다. 주소이므로 str1은 내보내지지 않았습니다.🎜🎜또한 내보내기를 사용하여 내보내는 것은 개체를 내보내는 것입니다🎜

🎜 2.1.2 Es Module🎜🎜🎜Javascript도 지속적으로 개발되고 진행되고 있습니다. 아니요, Es6 버전에는 Es Module 모듈🎜이 추가되었습니다. 🎜내보내기:🎜rrreee🎜가져오기: 🎜rrreee🎜import, node js 파일을 직접 실행하면 오류가 보고됩니다. 바벨 컴파일이 필요합니다🎜🎜비교하면 다음과 같습니다.🎜🎜CommonJs 문 동적으로 로드할 수 있고 코드는 런타임에 발생합니다.🎜🎜Es 모듈은 정적이며 명령문은 동적으로 로드할 수 없습니다. 파일 상단에서만 선언할 수 있고 코드는 컴파일 타임에 발생합니다.🎜🎜🎜2.1.3 타사 모듈🎜🎜 🎜Node에서 자체 핵심 모듈과 사용자 정의 모듈을 사용하는 것 외에도 🎜타사 모듈🎜🎜을 사용할 수도 있습니다.

这就需要提到 npm ,npm 是 Node 的包管理工具,已经成为了世界上最大的开放源代码的生态系统,我们可以下载各种包.

当然包管理工具还有yarn,但是我暂时只用过 npm,因为它随 node 一起按照提供.

2.2  Node 的事件循环

2.2.1 非阻塞I/O

Java、PHP 或者 .NET 等服务端语言,会为每一个客户端的连接创建一个新的线程。Node 不会为每一个客户连接创建一个新的线程,而仅仅使用一个线程。

console.log(&#39;1&#39;)
setTimeout(() => {
  console.log(&#39;2&#39;)
})
console.log(&#39;3&#39;)//输出132

Javascript 的代码是从上到下一行行执行的,但是这里就不会阻塞,输出3,再输出2

2.2.2事件循环

Node 的事件循环真的好久才弄懂一丢丢,看过很多博客,觉得理解 Node 的事件循环机制,结合代码及其运行结果来分析是最容易理解的。

libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程,形成一个 Event Loop(事件循环),以异步的方式将任务的执行结果返回给 V8 引擎。其中 libuv 引擎中的事件循环分为 6 个阶段,它们会按照顺序反复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。

console.log(&#39;start&#39;)
setTimeout(() => {//定时器1
  console.log(&#39;timer1&#39;)
  setTimeout(function timeout () {//定时器2
    console.log(&#39;timeout&#39;);
  },0);
  setImmediate(function immediate () {//定时器3
    console.log(&#39;immediate&#39;);
  });
  Promise.resolve().then(function() {
    console.log(&#39;promise1&#39;)
  })
}, 0)
setTimeout(() => {//定时器4
  console.log(&#39;timer2&#39;)
  Promise.resolve().then(function() {
    console.log(&#39;promise2&#39;)
  })
}, 0)
Promise.resolve().then(function() {
  console.log(&#39;promise3&#39;)
})
console.log(&#39;end&#39;)

可以 Node 上边运行一下

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调

I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调

idle, prepare 阶段:仅node内部使用

poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里

check 阶段:执行 setImmediate() 的回调

close callbacks 阶段:执行 socket 的 close 事件回调

理解:首先执行同步任务,所以会输出start end,poll阶段是事件循环的入口,有异步事件就是从这里进入的,同步任务执行完执行先微任务,输出 promise3,接下来就是 setTimeout了,由 poll阶段一步步到 timers 阶段,执行定时器1,输出 timer1,将定时器2和定时器3加入到队列里边,一旦执行一个阶段里的一个任务就立刻执行微任务队列,所以再输出 promise1,然后执行定时器4,如上输出timer2,promise2,结合事件再循环,到了 check 阶段,执行 setImmediate() 的回调,输出 immediate,再循环进行,到达 timer 阶段,输出 timeout

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

2.2.3浏览器的事件循环

浏览器和 Node 的事件循环是不一样的

打算用两张图和一段代码来解释浏览器的事件循环机制,

console.log(1)
setTimeout(()=>{console.log(2)},1000)//宏任务1
async function fn(){
    console.log(3)
    setTimeout(()=>{console.log(4)},20)//宏任务2
    //return Promise.reject()返回失败状态的,不会输出6,弄不清楚为啥
    return Promise.resolve()
}
async function run(){
    console.log(5)
    await fn()
    //console.log(6),
}
run()
//需要执行150ms左右
for(let i=0;i<90000000;i++){}
setTimeout(()=>{//宏任务3
    console.log(7)
    new Promise(resolve=>{
        console.log(8)
        resolve()
    }).then(()=>{console.log(9)})
},0)
console.log(10)
// 1 5 3 10 4 7 8 9 2

执行结果如(请忽略我的工具提示):

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

我们可以储备一些前置知识:JavaScript 是单线程的,任务可以分为同步任务和异步任务,像 console.log('1') 就是同步的,定时器 setTimeout,promise 的回调等就是异步的。同步的很好理解,就从上到下一行一行的执行下来,异步的就有点小复杂了,还会分为宏任务和微任务。

浏览器的事件循环机制就是:先执行同步任务,同步任务执行完成,就执行任务队列里面的任务,那任务队列里面的任务是哪来的呢?异步任务准备好了就会放进任务队列,你可以理解为,在任务队列里边宏任务和微任务都存在这一个队列结构管着它们。先后的话,同步任务执行完成后,任务队列里有微任务,则将微任务执行完,再执行一个宏任务,执行了宏任务可能又产生了微任务,这是就需要再执行完微任务任务。你可以将同步任务看成宏任务,这样就可以理解为,每执行完一个宏任务都要清理一遍微任务。

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.

위 코드에 대한 설명은 다음과 같습니다. 코드의 첫 번째 줄까지 실행하여 출력합니다. 1,执行到第二行代码 setTimeout 属于宏任务1,准备1000毫秒后加入任务队列,然后执行函数 run,输出 5,因为 await 的存在,我们需要等待 fn函数执行完毕,这里是通过 await 关键字将异步函数变成同步的,执行 fn 时输出 3,又出现一个 setTimeout 宏任务2,准备时间20毫秒,返回成功状态的Promise,输出 6,for 循环需要150ms,这是宏任务2,准备完毕,进入任务队列,继续向下,有一个 setTimeout 宏任务3,无需准备加入任务队列,执行最后一行代码,输出 10,至此同步任务全部执行完毕,接下来是异步任务了,任务队列是队列的数据结构,遵循先进先出的原则,此时任务队列中有宏任务2和宏任务3,先执行宏任务2,输出 4,再执行宏任务3,输出 7promise本身是同步的,输出 8,回调then 里边的代码是微任务,宏任务3执行后,发现有微任务存在,清理一边微任务,输出 9,整个流程经过1000毫秒后,宏任务1加入任务队列,输出 2

그것은 형님 블로그를 직접 보시면 이해가 더 쉬울 것 같습니다. 링크는 아래에 있습니다.

글을 잘 못쓰면 부끄럽지만 열심히 공부하겠습니다! ! !

더 많은 프로그래밍 관련 지식을 보려면 프로그래밍 비디오를 방문하세요! !

위 내용은 Nodejs의 모듈화와 이벤트 루프에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제