之前有段时间闲着无聊,去B站上简单了解了一下NodeJS,下面是根据一个视频记录的笔记,供大家参考,错误之处请大家帮忙斧正。侵删。
目录
1.1 官网介绍:https://nodejs.org/en/
4.3 路由机制 - url模块,path模块,querystring ——09.js
4.4 路由机制 小程序:——小小阿帕奇(静态资源文件) 10.js
6.2.2 require的模块中能够,如果有异步函数,将不会等待
0 参考教程
教程:http://www.runoob.com/nodejs/nodejs-tutorial.html
公开课教程:https://www.bilibili.com/video/av26470001?p=4 18h
教程:https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Express_Nodejs
1 NodeJs简介
1.1 官网介绍:https://nodejs.org/en/
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
Node.js是一个构建在chrome's V8 JS引擎上的一个JavaScript的运行环境
Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.
Node.js使用了事件驱动,非阻塞I/O模型,是轻量级的和高效的
Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.
Node.js的包生态(npm),是世界上最大的开源库生态系统
1.2 安装NodeJs
Node.js可以在任何操作系统上安装,底层是chrome V8引擎。
安装方法:下载run安装就可以
如何证明是否安装好了呢?
- 进入terminal
- $node -v //查看安装的版本号
为什么能够输入node命令呢?
- 在/usr/local/bin下面有node可执行程序
此时拥有了两个运行环境 :一个node,一个浏览器。若用浏览器解析,需要在h5文件中引用js文件,才能解析。
此时,证明了nodejs是一个JavaScript runtime(js运行环境)。
Node.js特点:单线程,非阻塞异步I/O,事件驱动
Node.js使JS的触角延伸到了服务器开发中,在node的世界中,我们关心的不再是JS操作网页上的DOM,制作交互动画,表单验证……而是服务器端的事情:HTTP请求的处理,GET请求,和POST请求,数据库增删改查,cookie和session等等。
1.3 NodeJS哲学
这是一个帅哥为了解决性能问题而发明了Node JS
异步I/O:绝大多数网站I/O是非常多的,I是数据的读取Input,O是数据的输出Output。但是在读取I/O时,CPU就被闲置了。
2008年Google发明了Chrome V8满足它关于高性能web服务器的想象,使用V8引擎解析JS程序,非常的快,并且V8隐性性能好,都是异步I/O,闭包特性方便,Ryan Dah1就把V8移植到服务器端。
2 开发服务器程序
2.1 helloworld 02.js
2.2 NodeJS是运行在服务器端
注意,此时查看源代码,在页面上查看不到(3+4),而是7,说明js运行了在服务器上,而不是前端的js。
Nodejs程序在服务器端运行。
2.3 NodeJS架构
NodeJS是一个让JS运行在服务器端的开发平台,它让JS的触角伸到了服务器端,但是Node似乎和其他服务器有点不同,如:PHP,ASP,JSP:
- Node.js不是一个独立的语言,与PHP,Perl,python,jsp,ruby的“既是语言,也是平台”不同,Node.JS使用JS进行编程。NodeJS是一个工具,语言仍是JS。
- 与PHP,JSP不同,Nodejs跳过来Apache,Tomcat,Nginx,IIS等HTTP服务器。它自己不用健在任何服务器软件上,NodeJs的许多设计理念与经典架构LAMP有着很大不同,可以提供强大的伸缩能力
- Nodejs自身哲学:花最小的硬件成本,追求更高的并发和处理性能
我们使用NodeJS开发服务器:GET请求,POST请求,POST请求参数的处理,数据库的增删改查,Cookie,session等等。
NodeJS没有根目录的概念!!!
3 Node.JS特定
3.1 单线程
在Java,PHP或者.net等服务器端语言中,会为每一个客户端连接创建一个新的进程,而每个进程需要耗费大约2MB内存,也就是说,理论上一个8GB内存的服务器可以同时连接的最大用户数为4000个左右,要让web应用程序同时支持更多的用户,就需要增加服务器的数量,而web英语程序的硬件成本当然就上升了。
Node.js不为每个客户连接创建一个新的线程,而是使用一个线程。当有用户连接,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。使用NodeJS,一个8GB内存的服务器可以同时处理超过4W用户的链接。
另外,单线程带来的好处还有:OS不需要再有线程创建、销毁的时间开销。
证明1:nodejs是单线程的 03.js
我们发现,所有用户共享了同一个变量a。
证明2:nodejs是单线程的 04.js
写了一个小程序,如果因为程序有错误,此时唯一的node进程将停止,此时会导致所有访问者无法访问
3.2 异步I/O
异步I/O:一个服务员,照顾很多客人。
当一个访问者访问服务器后,此时这个人就会去读取文本文件,此时CPU并没有被阻塞,其他人仍旧可以访问。当这个人的文本文件读取完毕之后,此时就会使用回调函数呈现页面。
只要I/O越多,NodeJS宏观上越并行。
NodeJS适合开发I/O多的任务,但不适合计算繁多的任务!
下图:
左图:多线程的宏观并行,CPU经常会等待I/O结束,CPU的性能会白白浪费;右图:单线程的宏观并行,单线程的程序,当并行极大的时候,CPU理论上的计算能力是100%。
因为Nodejs想在破的机器上也能够运行,所以剑走偏锋采用了单线程的模式,既然单线程必须异步I/O。
例如:挡在访问数据库取得的数据的时候,需要一段时间。在传统的处理机制中,在执行了访问数据库代码之后,整个线程都将暂停下来,等待数据库的返回结果,才能执行后面的代码,即:I/O阻塞了代码的执行,极大地降低了程序的执行效率。
由于Nodejs中采用非阻塞I/O,因此在执行力访问数据库的代码后,将立即转而执行其后面的代码,把数据库返回的结果放在回调函数中,从而提高了程序的执行效率。
当某个I/O执行完毕后,将以事件的形式通知执行I/O操作的线程,线程执行这个事件的回调函数,为了处理异步I/O,线程必须有事件循环,不断地检查有没有未处理的时间,依次予以处理。
3.3 事件驱动
事件驱动是Nodejs的底层机制,我们只需要了解Nodejs不会上错菜的原因就是事件驱动,有一个事件环。
事件环机制是Nodejs的底层机制,保证了Nodejs可以高效准确的运行,而不会紊乱。
4 路由机制
4.1 路由表
这是重点和难点
之前的案例,不管访问3000端口的什么网址,都能得到同样的结果。如果要根据用户访问的网址,给用户不同的显示,就需要使用req的url属性来进行判断。
此时,就知道了req里面是用户访问的请求信息,请求的网址是req,res是服务器的响应信息。
事实上,我们并不存在music文件夹、news文件夹,甚至我们可以伪装一个地址,但实际上并不存在music.html文件。
更加证明了Node.js是没有Apache的,是没有URL和真实物理文件映射关系的,这叫做顶层路由设计。能够支持顶层路由设计的比较流行的语言只有Node.js和Python。
甚至可以用正则表达式验证url是否匹配某一个模式,恶补正则:
补充:
js中正则:exec(): 最好用的;search();replace();test()
结合fs模块,做了一个小小的学生管理系统,顶层路由可以设计的非常漂亮,比如:
http://127.0.0.1:3000/student/100001
就是检索100001号的学生的情况,比如:
http://127.0.0.1:3000/student.php?xuehao=100003
不知道高明到哪里去了,后面将知道,这种风格的路由叫做RESTful类型的路由。
至此,顶级路由案例学完了07.js。
Noticed: res.end("")表示结束输出流,让网页停止转动。
4.2 静态资源的文件使用 08.js
我们现在不要进入Apaxhe的世界中无法自拔,Apache是一个怎样的世界:是一个物理文件和URL --对应的世界,比如:
但是,Nodejs没有根目录的概念!没有URL和物理文件一一对应的关系!
此时,如果我们的Html页面上,试图插入一个图片。
此时,才能往页面中插入图片。
我们要做一个静态资源管理器,但是需要储备知识,接下来介绍URL模块和path模块。
4.3 路由机制 - url模块,path模块,querystring ——09.js
如果一个URL比较完整,包括querystring部分(就是GET请求查询字符串传参部分),hash部分,那么此时
http://127.0.0.1:3000/b.html?id=123#123
req.url是:/b.html?id=123
即:querystring属于req.url,但是hash不属于。
但是,我们此时要得到文件名的部分,我不想要querystring,此时可以用正则提炼,但是太麻烦。
Node中提供了内置模块,url,path,querystring模块,都可以服务于url识别。
代码09.js:
网址是:
http://127.0.0.1:3000/haha/1.html?id=123&name=小明#abc
输出:
没有正确识别protocal协议,host主机名等等,但是能够正确识别pathname,query,
如果加上url.parse(req.url, true),此时querystring部分将会自动变为一个对象,方便我们存入到数据库中。
还有两个模块,path和querystring模块,都是服务于url提取的。
示意图:
他们还有其他方法,也暂时不用预习,后面项目会用的而别凶。
4.4 路由机制 小程序:——小小阿帕奇(静态资源文件) 10.js
创建一个文件夹myweb,我们的程序能够自动为文件里面呢的文件,图片,css,js加上路由
基本结构好思想:用户输入什么URL,我就真的用fs去读那个文件:
有几个不好用的地方:
第一点:首先是content-type的是。
就是说如果我们访问的是.html文件,那么content-type="text/html",如果是image,那么应该是"mime/jpeg".
解决方案:
第二点:不能自动识别首页:.index文件,
比如我们输入127.0.0.1:3000/b,希望读取myweb中b文件夹里面的index.html
解决方案:这种路径都没有拓展名,如果用户输入了一个url都没有拓展名,则自动那个不全一个index.html即可。
第三点:还有一个更大的问题,就是304的问题,304状态码not mofified, 当文件没有改变的时候,服务器要发出304状态码,拒绝传输文件;我们的小程序没有这样的识别,我们也不解决了,因为确实太复杂,还要利用session和cookie保存镜像。
5 小结
Q1: Nodejs是什么?
是一个JS的runtime,运行环境。实际上就是利用chrom v8引擎,将它一直到了服务器上,用它去开发服务器程序,可以提供HTTP服务。
Q2:Nodejs的主要特点?
A:Single-thread; non-blocking IO; Event driven事件驱动。
这三个特点是相辅相成的,是必须的选择。
Nodejs为了在低硬件服务器条件下高并发,所以就要减少内存消耗,所以剑走偏锋,选择了单线程,因此必须使用非阻塞I/O,因为只有一个线程,就必须当A用户去I/O时,处理B的业务的事情;B业务去I/O时,处理C的事情。。。。A、B、C都有回调函数。
为了让A、B、C都不乱套,每个人都进行合理调度,谁先来,处理谁,不能让B一直等待,处理C、D、E。。。。所以Node使用事件环,采用事件驱动来调度事件。
Q3: 下列那条语句是正确的,为什么?
A: var data = fs.readFile("./test.txt"); 错误
B: fs.readFile("./test.txt", function(err,data){…}); 正确
因为fs.readFile是异步方法,事实上Node的fs模块,mongdb模块基本上都是异步方法。一定要记住,异步函数不能通过return返回,不能通过等号来接受数据。必须通过回调函数传实参的模式来传输数据。
Q4: 说说http模块,它有哪些功能,有哪些方法?req和res各有什么属性和方法?
A:http模块用来提供http服务的,最常用的就是http.createServer((req,res)=>{}); 创建一个服务器。
req封装了HTTP上行请求的所有信息,常用属性req.url, req.connection.remoteAddress、 req.type等等。
Res是服务器应该给出的下行响应,常用方法是res.write(), res.write(), res.setHeder()…
Q5: 说说nodejs的顶层路由设计?
Apache和Nodejs的模式完全不一样。
Apache天生有静态资源服务,但是nodejs不行,nodejs必须使用路由清单给出明确的路由才行。
Q6: 说说常用的cmd命令?
cmd命令在windows下很弱,将来我们学习linux你会发现相当强大。
$clear - 清除屏幕内容
$cd - 切换文件夹
$ls- 列出当前文件夹下的所有文件
$node - 执行node.js文件
$ctrl+C - 打断进程
6 模块
模块module,指的是一些列有关系的js程序的集合
后面我们学习的MVC: model,view,control。
6.1 内置模块
Nodejs中内置了很多模块,可以直接用require进行引用,按照国际惯例来讲,你接受的名字最好和模块一样。
内置模块的引用使用require函数,引用是无路径的、无条件的。在任何目录下,都是通过require("http")来引用内置http模块的,而不需要require("../http")
内置模块是nodejs天生就有的。nodejs手册:http://nodejs.cn/api/
后面还会学习一些模块,注意记忆里面常用的方法和属性,比如:
url.parse()
querystring.parse()
6.2 自定义模块
每一个js文件就是一个模块,Node.js使用CMD(通用模块定义规范)。后面我们将知道webpack、seajs也使用哪个的是cmd规范,而Angular,requirejs等使用的是amd(异步模块定义)规范。
接下来了解Node.js模块的一些特性,而这些特性今后在学习webpack,seajs的时候也是通用的。
6.2.1 require谁,就运行谁
结论:require谁,就会运行谁。
注意:require("./test.js")中的。./坚决不能省略。因为如果省略了,代表读取node_module文件夹中的文件
6.2.2 require的模块中能够,如果有异步函数,将不会等待
结论:require文件中,如果有异步语句,此时nodejs仍然不会死等它结束,会返回执行文件中的程序,如果文件读取完毕,执行回调函数。
6.2.3 连续require
6.2.4 循环引用
如果A引用B,B又引用了A,会发生什么呢?
答案:NodeJS很智能,如果B重新引用了A,此时如同没有引用一样,会帮你自动抑制。
6.2.5 文件夹的层次
6.3 JS文件天生是隔离作用域的
结论:在Node.js中,每个js文件是一个单独的作用域。和DOM浏览器开发不一样,浏览器中, var a 此时a自动成为了window对象的属性,此时js文件和js文件可以共享作用域。 但是nodejs中,每个js文件是个铜墙铁壁,天生是隔离作用域的。
6.4 使用exports.xx = xx 暴露属性出去
我们如果想要把元素、变量、函数向外暴露 ,此时可以使用exports.** = **向外暴露,
此时引用这个模块的文件将用等号来接受,等号左边的变量将自动成为exports对象。
上面的例子中,var test 这个变量就是exports对象。
这种暴露非常好用,通常用于类似的功能向外暴露。比如mathtool.js:
mathtool.js
01.js
6.5 使用module.exports暴露
刚刚我们一个js文件中向外暴露了多个东西,比如sum, average,pingfang等等。但是如果一个js文件中,仅仅向外暴露一个东西,此时用module.exports不方便。比如我们要向外暴露一个类。
结构:
调用函数的时候,要使用People.People()写法,非常不方便。
原因是什么?因为js文件中要暴露很多东西。
此时,Nodejs建议使用module.exports= People.
6.6 神奇的node_modules文件夹
如果我们写一个引用,没有写./:
此时表示引用node_modules文件夹中的文件。
并且更为神奇的是,node_modules文件夹里面的模块,在引用的时候不需要考虑路径,只需要确保node_modules文件夹需要引用的模块的js文件里路径的任何祖先路径中。
不仅如此,比如我们haha.js文件,它不是要引入test.js吗,只要node_modules文件夹出现在任何haha.js的祖先目录中都可以
但是,其他的非祖先文件夹不行!!!
但是有一个路径更神奇,叫做系统环境路径,在:
C:\Users\你的电脑名字\AppData\Roaming\npm\node_modules
天下无人不识君,这个是全局路径。
当省略文件名的时候,会自动识别index.js文件
做对这个题目,就说明已经会了
Q: 下列的四种require写法,实际引入的是谁?
Require("./a.js") -- 引入当前文件夹中的a.js
Require("a.js") -- 引入node_modules中的a.js文件
Require("a") --引入的是node_modules文件夹中的a文件夹的index.js文件
Require("./a") --引入的是当前文件夹中的a文件夹中的index.js文件。
7 npm的世界
我们刚刚封装了一个数学函数mathtool很好用,此时node开发中也发现了这个事情,如果让全球开发者贡献自己的实用模块那该多好。让大家不要重复造轮子。
说一下什么是模块,一个math-tool就是一个模块。但是如果5、6个js文件都是服务于数学方面的,那它们的整体又可以作为一个大模块。例如:
Math-tool整体就是一个大函数。
所以Nodejs主导了一个社区,叫做npm(node package management, node包管理器)。
7.1 npm使用
淘宝镜像: https://npm.taobao.org/
此时就可以在上面寻找我们要的东西,比如现在有需求,比如:把公历变为农历。第一时间就要想到,我不要重复造轮子,要去npm社区找找看!
搜索“农历”就可以,点击详情,就可以看到api
直接使用npm命令就可以下载,npm是随着node安装的,
$npm install solarlunar
注意:一定要联网。此时solarlunar这个模块就会被自动的下载到你项目的node_modules文件夹。
非常神奇!此时通过查看API,可以实现我们的业务了。
7.2 依赖
很明显,现在的项目03.js依赖了两个别人的包,分别是:chinese-finance-number,solarLunar。显然的,node_modules是不能删除的,否则一定会报错。
但是我们现在用U盘拷贝项目给别人,此时完全没有必要去拷贝node_modules文件夹,因为这里面的东西在npm线上啊,npm很稳定,随时可以下载!
npm有一个创造性的举动,可以让开发者声明自己的项目的全部依赖,可以告诉别人该项目依赖什么文件。
创建package.json中,定义项目依赖的包,如下:
将package.json放入到项目的根目录中,再
$ npm install命令就可以自动安装项目需要的包
所以package.json文件非常重要,就是项目的身份证!事实上,里面还有很多配置项。
可以用:
$npm init
此时npm将会有一个表单,引导你去创建一个较为完整的package.json文件。此时,只需要回答一些问题即可,如果默认选项则按回车即可。会帮我们生成:如下。
版本符号:
我们看看npm的手册: https://docs.npmjs.com/files/package.json
常用举例:
但是还是不方便,如果我们安装一个以来的时候,能够自动进入到package.json中,就好了!
$npm install solarlunar --save
可以帮助我们自动将依赖项solarlunar加入到package.json中。
如果想要限制版本
$npm install solarlunar#^1.0.0 --save
卸载
$npm uninstall **
总结
$npm init //帮我们创建一个package.json文件的,项目开发的第一件事就是这个。
$npm install //根据package.json文件,安装全部的项目依赖
$npm install solarlunar //install solarlunar package
$npm install solarlunar -- save// install package and write dependencies into package.json.
7.3 全局安装
$npm install package_name -g
此时,这个包会被安全到全局。
Mac下地址: /usr/local/lib/node_modules
可以通过$npm list -g查看所有全局安装的模块。
一些CLI(命令行程序)、工程化的东西将被安装到全局。随着学习的深入,我们将遇见。
7.4 淘宝镜像
淘宝为了方便中国程序员对抗GFW,就提供了淘宝镜像cnpm。你可以用此代替官方版本(只读),同步频率目前5分钟一次,以保证与官网同步。
如何设置淘宝镜像呢?如下:
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
今后所有的npm活动操作只要用cnpm代替即可。
最后说一句,有很多包非常大,自己也依赖别人,不用怕,他们的依赖也写在了他们自己的package.json文件中。
cnpm,npm可以深入读取每个包的package.json, 能够自动将其依赖都下载下来。
npm是个怎样的世界?就是站在巨人的肩膀上,而他也站在别人的肩膀上。。。。
8 GET请求和POST请求
8.1 GET请求
Get 请求传参数通过URL,POST请求传参通过HTTP上行报文。GET请求不安全,方便分享网址;POST请求安全,不方便分享往回,内容信息无限长。
GET请求的参数在URL中:
Nodejs如果获得get请求,实际上就是如何解析URL,昨天已经讲过了。就是:
安装一个小型服务器,因为只有服务器环境才能发出Ajax请求。
$ cnpm install serve-static --save
$ cnpm install finalhandler --save
8.2 POST请求
8.3 formidable模块
npm上可以下载一个formidable的模块,用来处理post请求。甚至可以处理图片、zip等多媒体文件的上传。
APi文档: https://npm.taobao.org/package/formidable
Step1 - install dependency
$cnpm install --save formidable
Step2 - 通过看API来学习和使用
9 Express框架入门
传统Node.js的缺点:
- 路由不方便制作,尤其是正则表达式路由
- 静态资源服务不方便
- 页面呈递不方便,没有模板引擎
官网:https://expressjs.com/zh-cn/
Express 高度包容、快速而极简的 Node.js Web 框架。 是一种保持最低程度规模的灵活 Node.js Web 应用程序框架,为 Web 和移动应用程序提供一组强大的功能。
Express 提供精简的基本 Web 应用程序功能,而不会隐藏您了解和青睐的 Node.js 功能。
基于Express的流行框架
9.1 基本使用
$cnpm install express --save
然后就可以写基本的demo了,基本上都是固定动作,没什么好解释的
看一下中间件的特写:
表示当用get请求访问/news时做的事情,此时输出使用res.send()而不是res.end().好处是自动变成UTF-8编码。
并且express是自动使用pathname与/news进行比较,就是说会自动过滤querystring,hash等等。
我们可以用冒号:来引导一个变量,此时特别方便做一个正则的路由:
9.2 静态路由
一个中间件解决问题:
App.use("/jingtai", express.static("public"));
use表示使用中间件,刚才的get表示在get请求下使用中间件。use是无论什么请求都会使用。
此时我们放置在public文件夹中的xx.png会被路由到/jingtai下:
http://127.0.0.1:3000/jingtai/xx.png
静态资源表放在最后,省得拦截别的!
9.3 使用某一个页面
例如:当我们访问/的时候,我们就想要呈递一个public/haha.html页面,这是一个经常的事情
此时不需要那个逻辑了:
而是:
//express的常用的send函数
res.sendFile(页面绝对路径)//发送页面给用户
res.send(content)//发送内容给用户
9.4 模板引擎
express可以像php一样使用模板引擎,此时最好用的模板引擎就是ejs
全称:Embedded JavaScript templates 嵌入式JS模板
API: https://npm.taobao.org/package/ejs
$cnpm install ejs --save
使用案例1:
发生了什么?
此时当用户访问/的时候,会自动使用views文件夹中的shouye.ejs当做模板。字典就是后面传入的json。
模板是后台服务器填充的,访问者休想看到源代码。
使用案例2:
views/shouye.ejs
输出效果:
很方便,任何JS中能够使用的数据类型都能传:
08.js
views/shouye.ejs