当前位置: 首页 > 后端技术 > Node.js

Node.js系列:原生Node.js应用

时间:2023-04-03 15:15:02 Node.js

NativeNode.jsapplicationNode.js是一个基于ChromeV8引擎的JavaScript运行环境Node.js使用事件驱动、非阻塞I/O模型使其轻量高效Node.js包管理器npm,是世界上最大的开源图书馆生态系统?本文主要介绍构建Node.js应用的基本步骤和模块,假设你已经对Node.jsApi有了一定的了解?本文引用部分代码作为例子,如果想看全部源码,请到github查看(如果觉得有帮助,欢迎star)模块架构设计整个Node.js应用的架构设计Node.js应用composition导入模块:通过require命令导入Node.js模块创建服务器:使用服务器监听客户端请求接收请求并响应请求:接收客户端的HTTP请求并返回响应数据//通过require导入http模块,并赋值实例化HTTP到http变量consthttp=require('http');//导入url模块,用于解析数据consturl=require('url');functionylone(router,handleObj){consthostname='127.0.0.1';常量端口=7777;//http.createServer(function(){})方法创建一个服务器并返回一个对象constserver=http.createServer((req,res)=>{constpath=url.parse(req.url);constpathName=path.pathname;//处理每个node.js自动请求favicon.icoif(pathName!=='/favicon.ico'){constcontent=router(handleObj,pathName,res,req);}});//server.listen()方法绑定主机和端口-server.listen(port,hostname,()=>{console.log(`服务运行在${hostname}:${port}`);});}exports.ylone=ylone;事件驱动回调http.createServer((req,res)=>{...})是一个典型的回调,其实Node.“转”圈就像写一个PHP应用:每当一个请求进来,服务端(比如Apache)就会为这个请求新建一个进程,并在Node.js中从头到尾执行相应的PHP脚本,不管of当一个新的请求到达指定的端口时,我们在创建服务器时传递的函数会被调用index.js调用相应的模块来引导并启动应用程序模块化代码意味着我们将提供其功能的部分(例如函数)导出到请求该模块的脚本constserver=require('./http');constrouter=require('./route');consthandle=require('./requestHandle');varhandleObj={};//entryCasehandleObj['/']=handle.hello;//非阻塞CasehandleObj['/vlone']=handle.vlone;//postCasehandleObj['/supreme']=handle.supreme;//getCasehandleObj['/adidas']=handle.adidas;server.ylone(router.router,句柄对象);路由为了处理不同的http请求,我们需要创建一个“路由选择”的路由模块,为路由提供请求的url和其他需要的get、post参数,然后路由根据这些数据执行相应的代码。需要的数据都在http.createServer((req,res)=>{...})在req参数中,为了解析req,需要在index.js中引入额外的url和querystringNode.js模块导入路由对象,将路由方法传递给http应用,在http.createServer((req,res)=>{...})解析req参数,然后调用router方法,理解下面的函数式编程:将router对象传递给index,在index中将router方法传递给http,因为http并不关心router的方法来自哪里,只需要你执行该方法然后完成业务即可,但是首先你需要保证你有这个对象。函数式编程最基础最核心的就是思想的转换,从名词到动词,从对象到方法,行为驱动执行。functionrouter(handleObj,pathName,res,req){if(typeofhandleObj[pathName]==='function'){returnhandleObj[pathName](res,req);}}else{res.writeHead(200,{'Content-type':'text/plain'});constcontent='404未找到';res.write(内容);重发();}}exports.router=路由器;分发路由给请求处理函数需要新建resquestHandlers模块,封装各个Process函数对应不同的请求在JavaScript中,路径和方法的映射关系是通过对象键值对封装的在C++或C#中,一个对象是指一个一个类或结构的实例,而对象会根据其实例化而具有模板不同的属性和方法在JavaScript中,对象是键值对的集合。在入口文件(index.js)中引入requestHandle,同时声明一个操作对象(handleObj),用于存放路径->方法映射关系,最后把路由方法和操作对象传递给服务器应用程序(http.js)。在服务端应用中,获取浏览器请求的路径,调用路由方法(router),操作对象(handleObj)和请求路径在路由(route.js)中作为参数传递,获取路径对应的函数。由于文章篇幅,这里只展示自执行功能的关键代码。有关源代码,请参阅githubconst{exec}=require('child_process');constquerystring=require('querystring');consturl=require('url');函数createHttp(type,res,val){constcontent=val;constconType={plain:'text/plain;charset=utf-8',html:'text/html',};//为隐式响应头设置值res.writeHead(200,{'Content-type':conType[type]});//发送响应体res.写(内容);//http完成响应res.end();}...somethingelsefunctionvlone(res){exec('node--version',(error,stdout,stderr)=>{if(error){console.log(error,stdout,stderr);return;}constcontent=stdout;consttype='plain';createHttp(type,res,content);});}...其他阻塞与非阻塞A()方法读取文件,所以需要一定的响应时间。B()方法代表其他需要执行的代码阻塞:A()执行期间,B()处于等待状态。当A()访问文件数据准备就绪后,B()开始执行。从上图可以看出,从进行系统调用到将数据报复制到应用进程缓冲区的整个过程都是阻塞的,直到数据报被复制到应用进程缓冲区。用户空间完成后,用户进程会解除阻塞状态,继续执行下一个应用程序。优点:可以及时无延迟地返回数据,方便调试。缺点:需要等待非阻塞:A()执行期间,B()同时执行,当A()访问文件数据准备好后,A()才会执行。从上图可以看出,如果应用程序在调用时,如果数据报没有准备好,首先会返回一个错误信息(EWOULDBLOCK),此时当前进程可以不阻塞地执行其他方法。而A()会轮询内核返回buffer数据是否就绪。优点:无需等待,当前线程可以处理多个任务缺点:增加任务完成的响应延迟,因为任务可能在两个轮询间隔内完成,导致整体数据吞吐量较低。以非阻塞方式请求响应。当前应用交互:(requesthandler->requestrouting->server)会将requesthandler的内容(requesthandler最终会显示给User的内容)返回给HTTPserver当前交互方式的问题在于,如果有Node.js在requesthandler中封装了一个非阻塞的方法A(),那么服务器在A()的阻塞过程中已经返回数据了,不会等到A()执行完。为了解决以上问题,采用非阻塞方式请求和响应。与之前将数据传递给服务器的方法相比,现在我们需要将服务器(响应对象)传递给生成数据的应用程序。里面,数据准备好后,返回响应数据,这样可以同时请求两条路径(其实是触发了两个函数方法),B()不会因为执行时间长而处于等待状态A()处理post请求的时间创建一个表单元素,设置表单提交方式为post,每当用户提交表单时,都会触发supreme()方法处理post请求。一般采用异步非阻塞的方式,因为post请求一般比较重,你无法控制用户输入的数据量,如果采用阻塞的方式处理,势必导致用户操作阻塞。为了实现非阻塞,Node.三个事件:数据事件(新数据块到达时触发)、结束事件(所有数据接收完毕时触发)通过在请求对象上注册一个监听器(listener)来告诉应用程序在post事件结束时应该触发什么触发回调函数处理get请求,通过Node.js封装的url对象解析url参数,获取关键数据url.parse()如果第二个参数parseQueryString为true,query属性总会通过querystring模块的parse()方法生成一个对象的一些片段。写好Node.js脚本(如ylone.js)后,通过nodeylone.js命令执行当脚本在浏览器访问指定地址(如http://localhost:7777/)时,表示向服务器发送请求,从而触发服务器创建时的回调函数。在访问网页时(如http://localhost:7777/),控站可能会输出两次req的数据,这是因为大多数浏览器在访问网页时都会尝试读取favicon.ico文件。浏览器每次发送请求,默认都会请求/favicon.ico。在http中过滤,不做任何操作。如果你想在Node.js中传递一个html片段并在浏览器上渲染,你需要设置res.writeHead(200,{'Content-type':'text/plain'})Content-typeissettotext/htmlNode.js返回数据(response)在浏览器中显示乱码,通过res.writeHead(200,{'Content-type':'text/plain;charset=utf-8'})加上charset=utf-8配置解决方案——尊重Node.js——