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

javascript开发后端程序的神器nodejs

时间:2023-04-03 15:07:54 Node.js

介绍虽然javascript一直可以作为服务端编程语言使用,但更多的是作为客户端编程语言展示在世人面前。可能javascript本身忘记了它也可以做服务端编程,直到2009年nodejs的诞生。但是早期javascript引擎的执行效率比较低,所以javascript只能做dom操作。随着ajax的兴起和现代web2.0技术的发展,主流浏览器开发者想方设法提高javascript的执行效率。最后,ChromeV8出现了。ChromeV8是Chromium项目的开源JavaScript引擎,使得javascript的执行效率得到了极大的提升。nodejs借助V8浴火重生。Nodejs从诞生之日起就获得了极大的关注。仍然有很多JavaScript开发人员。而一门语言可以同时使用前端和后端是多么吸引人。nodejs从2009年发展到2020年的nodejs14,经历了11年的历史,和它的前身javascript相比还很年轻,但是因为它的开放性和包容性,nodejs正以非常快的速度向前发展。nodejs简介借助V8引擎和一套异步I/O原生函数,nodejs大大提高了nodejs的处理效率。异步IO大家应该都很清楚了。与同步IO相比,线程不需要阻塞,可以处理其他更有意义的事情。当响应返回时就恢复操作,所以不会浪费CPU时间。简单看一下nodejs的IO模型:好的语言需要有好的生态来支撑,因为语言本身只能提供最基本的操作,我们还需要第三方系统来丰富生态语言。nodejs的npm仓库承载着全球最大的开源库生态。基本上使用nodejs就可以实现大部分需要的功能。nodejs的另一个特点是简单。考虑一下我们最常用的Web应用程序。如果用java写,就很麻烦了。您还需要一个Web服务器。在nodejs中,一切都是那么简单:consthttp=require('http')consthostname='127.0.0.1'constport=3000constserver=http.createServer((req,res)=>{res.statusCode=200res.setHeader('Content-Type','text/plain')res.end('welcometowww.flydean.com\n')})server.listen(port,hostname,()=>{console.log(`pleasevisithttp://${hostname}:${port}/`)})上面的代码创建了一个监听3000端口的web服务,我们首先介绍http模块,用于http处理。然后使用http的createServer()方法将创建一个新的HTTP服务器并返回它。在createServer方法内部,我们可以设置要返回的对象。最后,启用server.listen函数来监听特定的端口和服务器。当服务就绪后,会调用后续的回调函数来执行特定的命令。每当收到新请求时,都会触发请求事件。请求事件可以传递两个参数:请求是一个http.IncomingMessage对象,它提供有关请求的详细信息。response是一个http.ServerResponse对象,用于将数据返回给调用者。在上面的例子中,我们没有使用request,而是直接使用response构造返回对象。我们设置statusCode和header,最后使用end关闭响应。这是一个简单易用的nodejs程序。nodejs的运行环境Nodejs作为js的一种,是一种解释型语言。一般来说,解释型语言有两种运行模式。一种是直接运行,一种是开一个解释性环境在里面运行,nodejs也不例外。直接运行非常简单。我们写好nodejs程序后,比如app.js,直接运行如下:nodeapp.js如果直接执行node命令,会打开REPL模式:nodeWelcometoNode.jsv12.13.1.Type”。help”获取更多信息。>REPL也称为RunEvaluationPrintLoop,是一种编程语言环境(主要是控制台窗口),它将单个表达式作为用户输入,并在执行后将结果返回到控制台。REPL是做什么的?首先,我们可以直接在REPL中运行某些测试方法,验证输出。例如:>console.log('www.flydean.com');www.flydean.com此外,REPL还有一些比较实用的功能。我们知道在JS中一切都是对象,比如我们上面提到的http对象,如果我们想知道http对象的大概结构怎么办?只需在REPL环境中输入http:>http{_connectionListener:[Function:connectionListener],METHODS:['ACL','BIND','CHECKOUT','CONNECT','COPY','DELETE','GET','HEAD','LINK','LOCK','M-SEARCH','MERGE','MKACTIVITY','MKCALENDAR','MKCOL','MOVE','NOTIFY','OPTIONS','PATCH','POST','PROPFIND','PROPPATCH','PURGE','PUT','REBIND','REPORT','SEARCH','SOURCE','SUBSCRIBE','TRACE','UNBIND','UNLINK','解锁','取消订阅'],STATUS_CODES:{'100':'继续','101':'切换协议','102':'处理中','103':'早期提示','200':'确定','201':'已创建','202':'已接受','203':'非权威信息','204':'无内容','205':'重置内容','206':'部分内容','207':'多状态','208':'已报告','226':'IM使用','300':'多项选择','301':'永久移动','302':'找到','303':'见其他','304':'未修改','305':'使用代理','307':'临时重定向','308':'永久重定向','400':'错误请求','401':'未经授权','402':'需要付款','403':'禁止','404':'NotFound','405':'MethodNotAllowed','406':'NotAcceptable','407':'ProxyAuthenticationRequired','408':'RequestTimeout','409':'Conflict','410':'消失','411':'要求的长度','412':'前提条件失败','413':'有效载荷太大','414':'URI太长','415':'不支持的媒体类型','416':'范围不可满足','417':'期望失败','418':“我是茶壶”,'421':'错误的请求','422':'不可处理的实体','423':'Locked','424':'FailedDependency','425':'UnorderedCollection','426':'UpgradeRequired','428':'PreconditionRequired','429':'TooMany请求'、'431':'请求标头字段太大'、'451':'因法律原因不可用'、'500':'内部服务器错误'、'501':'未实现'、'502':'错误网关”、“503”:“服务不可用”、“504”:“网关超时”、“505”:“不支持HTTP版本”、“506”:“变体也在协商”、“507”:“存储空间不足”','508':'检测到环路','509':'超出带宽限制','510':'未扩展','511':'需要网络认证'},Agent:[功能:Agent]{defaultMaxSockets:无限},ClientRequest:[功能:ClientRequest],IncomingMessage:[功能:IncomingMessage],OutgoingMessage:[功能:OutgoingMessage],服务器:[功能:服务器],ServerResponse:[功能:ServerResponse],createServer:[功能:createServer],get:[Function:get],request:[Function:request],maxHeaderSize:[Getter],globalAgent:[Getter/Setter]}直接输出http对象的简洁结构,我们也可以使用tab按钮自动补全http的方法:ClientRequesthttp.IncomingMessagehttp.METHODShttp.OutgoingMessagehttp.STATUS_CODEShttp.Serverhttp.ServerResponsehttp._connectionListenerhttp.createServerhttp.gethttp.globalAgenthttp.maxHeaderSizehttp.requestPREL还支持一些特定的点运算:>.help.breakSometimes你卡住了,这让你出来。clearAliasfor.break.editorEntereditormode.exitExitrepl.help打印这个帮助信息load从一个文件中加载JS到REPL会话中打印上次操作的结果。processprocess对象是一个全局变量,它提供有关当前Node.js进程的信息并控制它。作为一个全局变量,它始终可供Node.js应用程序使用,无需require()。也可以使用require()显式访问它。因为process代表的是nodejs的进程信息,可以处理进程终止,读取环境变量,接收命令行参数等。要终止进程,我们先来看看如何使用process终止进程:process.exit(0)0表示正常退出。当然,我们可以传入不同的退出码来表示不同的含义。通常,如果没有异步操作挂起,Node.js会以状态码0退出。否则,它会以以下状态代码退出:1UncaughtException-未被域检测到或未被“uncaughtException”事件处理程序处理的未捕获异常。2-未使用(Bash保留用于内部滥用)3内部JavaScript解析错误-Node.js中的JavaScript源代码在启动过程中导致语法解析错误。通常仅在开发Node.js本身时出现。4InternalJavaScriptExecutionFailed-当引导进程在Node.js中执行JavaScript源代码时,无法返回函数值。通常仅在开发Node.js本身时出现。5fatalerror-V8中存在致命错误。通常,从stderr打印的消息以FATALERROR为前缀。6非函数的内部异常处理-发生内部异常,但内部异常处理函数被设置为非函数,或无法调用。7内部异常处理运行时失败-存在无法捕获的异常,处理函数本身在尝试处理时抛出错误。例如,如果'uncaughtException'或domain.on('error')处理程序抛出错误。8-未使用。在以前的Node.js版本中,退出代码8有时表示未捕获的异常。9-Unavailableparameter-未定义未知选项,或未填写所需选项。10InternalJavaScriptruntimefailure-调用引导函数时,引导进程在Node.js中执行JavaScript源代码并抛出错误。通常仅在开发Node.js本身时出现。12UnavailableDebugParameters13UnfinishedTop-LevelAwait:await传入的Promise从未调用过resolve方法128ExitSignal-如果Node.js收到一个致命信号,例如SIGKILL或SIGHUP,那么它的退出代码将是128plus信号的代码值。比如signalSIGABRT的值为6,那??么预期的exitcode就是128+6或者134。我们可以通过进程的on方法来监听signal事件:process.on('SIGTERM',()=>{server.close(()=>{console.log('进程已终止')})})这是什么信号?信号是一个POSIX内部通信系统:通知被发送到进程以通知它们已经发生的事件。或者我们可以在程序内部发送这个信号:process.kill(process.pid,'SIGTERM')env因为process进程是在和外部环境打交道,所以process提供了env属性,这个属性携带了启动时设置的所有设置进程环境变量。默认情况下,env中的NODE_ENV设置为development。process.env.NODE_ENV//"development"我们可以通过修改这个环境变量来切换nodejs的不同运行环境。argvprocess提供了argv来接收外部参数。例如:nodeapp.jsjoeargv是一个包含所有命令行调用参数的数组。在上面的示例中,第一个参数是节点命令的完整路径。第二个参数是正在执行的文件的完整路径。所有其他参数从第三个位置开始。获取joe,我们可以这样做:constargs=process.argv.slice(2)args[0]在key=value的情况下,我们可以这样传递参数,使用minimist库处理参数:nodeapp.js--name=joeconstargs=require('minimist')(process.argv.slice(2))args['name']//joeCLI交互从nodejs7开始,nodejs提供了readline模块,可以从process中获取。stdinInput:constreadline=require('readline').createInterface({input:process.stdin,output:process.stdout})readline.question(`你好吗?`,answer=>{console.log(`${answer}!`)readline.close()})如果你需要更复杂的操作,你可以使用Inquirer.js:constinquirer=require('inquirer')varquestions=[{type:'input',name:'你好',消息:“你好吗?”}]inquirer.prompt(questions).then(answers=>{console.log(`${answers['hello']}!`)})exportsmodulenodejs有内置的Module系统,当我们需要使用其他库提供的功能,我们可以使用require来引入其他库暴露的模块。但是前提是lib需要是public的,也就是exports对应的module出来。有两种方法可以导出nodejs对象module.exports和添加对象作为导出的属性。先看第一种方式,square模块在square.js中定义:module.exports=classSquare{constructor(width){this.width=width;}area(){returnthis.width**2;}};在下面的示例中,bar.js使用导出Square类的square模块:constSquare=require('./square.js');constmySquare=newSquare(2);console.log(`mySquare的面积是${mySquare.area()}`);再看第二种方式,定义一个circle.js:const{PI}=Math;exports.area=(r)=>PI*r**2;exports.circumference=(r)=>2*PI*r;使用:constcircle=require('./circle.js');console.log(`半径为4的圆的面积为${circle.area(4)}`);两者都可以导出一个具体的模块,但是module.exports只会导出一个具体的对象,而exports是将对象添加为exports的一个属性,我们还需要根据属性名找到对象的属性。除了上面我们提到的http和process,nodejsAPI还提供了很多其他非常好用的API:除了基本的nodejs框架,nodejs还有很多优秀的框架。有了这些框架,我们可以创建nodejs程序构建起来更容易,功能也更强大。像AdonisJs、express、koa、Socket.io等等。本文作者:flydean程序的那些事本文链接:http://www.flydean.com/nodejs-kickoff/本文来源:flydean的博客欢迎关注我的公众号:《程序与事》最通俗的解读,最深刻的干货,最简洁的教程,还有很多你不知道的小技巧等你来发现!