Node.js
基本概念
浏览器与服务器的交互流程
- 浏览器通过地址栏发出请求
- 通过DNS服务器解析,得到域名对应的ip地址
- 根据ip地址,访问服务器具体的某个文件
- 服务器响应这个具体的文件
- 浏览器获取响应,进行显示
前端开发的页面存放在服务器,在客户端执行;
前后端开发
前端开发:以浏览器为宿主环境,结合 HTML、CSS、Javascript等技术,而进行的一系列开发,通常称之为前端开发。
服务器端开发:HTTP服务器可以结合某一编程语言处理业务逻辑,由此进行的开发,通常称之为服务端开发。
nodejs:服务端的javascript开发,用于开发服务端程序的。
前端学习Node.js
- 为什么要学习服务端的开发?
- 通过学习Node.js开发理解服务器开发、Web请求和响应过程、 了解服务器端如何与客户端配合作为前端开发工程(FE)需要具备一定的服务端开发能力;
- 了解什么是服务端渲染?
- 了解服务端如何编写接口?
- 通过学习Node.js开发理解服务器开发、Web请求和响应过程、 了解服务器端如何与客户端配合作为前端开发工程(FE)需要具备一定的服务端开发能力;
- 全栈工程师的必经之路
- 服务器端开发语言有很多,为什么要选择nodejs
- 降低编程语言切换的成本(nodejs实质上用的还是javascript)
- NodeJS是前端项目的基础设施,前端项目中用到的大量工具,都是基于nodejs实现的
- nodejs在处理高并发上有得天独厚的优势
- 对于前端工程师,面试时对于nodejs有一定的要求
Node.js详解
Node.js是一个Javascript运行环境(runtime environment),发布于2009年5月,由Ryan Dahl开发,实质是对Chrome V8引擎进行了封装。Node.js对一些特殊用例进行优化,提供替代的API,使得V8在非浏览器环境下运行得更好。
- Node.js 不是一门变成语言;
- Node.js 是JavaScript的运行环境;
- Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
- Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。
- Node.js 的包管理器 npm,是全球最大的开源库生态系统。
- Node.js 的本质:不是一门新的编程语言,nodejs是javascript运行在服务端的运行环境,编程语言还是javascript;
nodejs与浏览器端js的区别
nodejs是基于chrome v8引擎的,因此nodejs可以和浏览器一样执行js代码,但是二者执行的js还是有一定区别的。
- 浏览器端
- ECMAScript
- 浏览器提供的BOM和DOM方法
- Node.js
- ECMAScript
- 不能使用BOM和DOM方法
- 使用Node.js提供的API
nodejs可以干什么?
- 开发服务端程
- 开发命令行工具(CLI),比如npm,webpack,gulp等
- 开发桌面应用程序(借助 node-webkit、electron 等框架实现)
环境变量
- 当要求系统运行一个程序 而没有告诉它程序所在的完整路径时;
- 首先在当前目录中查找和该字符串匹配的可执行文件
- 进入用户 path 环境变量查找
- 进入系统 path 环境变量查找
- 配置环境变量:
1 | 配置环境变量: |
运行Node.js
方式一:REPL介绍
- REPL 全称: Read-Eval-Print-Loop(交互式解释器)
- R 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
- E 执行 - 执行输入的数据结构
- P 打印 - 输出结果
- L 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。
- 在REPL中编写程序 (类似于浏览器开发人员工具中的控制台功能)
- 直接在控制台输入
node
命令进入 REPL 环境
- 直接在控制台输入
- 按两次 Control + C 退出REPL界面 或者 输入
.exit
退出 REPL 界面- 按住 control 键不要放开, 然后按两下 c 键
方式二:使用node执行js文件
- 创建js文件
helloworld.js
- 写nodejs的内容:
console.log('hello nodejs')
- 打开命令窗口
cmd
- shift加右键打开命令窗口,执行
node 文件名.js
即可 - 给vscode安装
terminal
插件,直接在vscode中执行
- shift加右键打开命令窗口,执行
- 执行命令:
node helloworld.js
【注意:在nodejs中是无法使用DOM和BOM的内容的,因此document, window
等内容是无法使用的。】
global模块-全局变量
在Node.js中,global是一个顶层对象;在Node.js中可以直接访问global的方法和属性;不需要加载;
全局变量说明
- 浏览器全局变量
- 浏览器的顶层对象是window;window的顶层作用域和全局作用域重合;
- 所以在全局作用域中声明的变量就是window的变量;可以通过window来访问;
- Node.js全局变量
- Node.js的顶层对象是global;global是一个模块,顶层作用域;全局变量;
- 而每一个js文件是一个单独的模块;每一个单独的模块是一个全局作用域(模块作用域)–相对自身;
- 所以在全局作用域中声明的变量,并不是global的变量;global不能访问全局作用中 var 声明的变量;
常用的global属性
1 | console: 用于打印日志 |
【注意:除了global模块中的内容可以直接使用,其他模块都是需要加载的。】
fs模块-文件系统
Node.js提供的核心模块;操作文件的模块;
fs模块不是全局的,不能直接使用。因此需要导入才能使用。
读取文件
语法:fs.readFile(path[, options], callback)
1 | fs.readFile("data.txt", "utf8",function(err, data){ |
写文件
语法:fs.writeFile(file, data[, options], callback)
1 | /* |
追加文件
语法:fs.appendFile(path, data[, options], callback)
1 | /* |
关于Buffer对象
1 | 一个二进制数 01010100 0位 |
提供了同步和异步操作两种方法;
fs中所有的文件操作,都提供了异步和同步两种方式
异步方式:不会阻塞代码的执行
1 | //异步方式 |
同步方式:会阻塞代码的执行
1 | //同步方式 |
【总结:同步操作使用虽然简单,但是会影响性能,因此尽量使用异步方法,尤其是在工作过程中。】
path模块-路径操作
关于路径,在linux系统中,路径分隔符使用的是/,但是在windows系统中,路径使用的\在我们拼写路径的时候会带来很多的麻烦,经常会出现windows下写的代码,在linux操作系统下执行不了,path模块就是为了解决这个问题而存在的。
var path = require("path");
模块导入
在读写文件的时候,文件路径可以写相对路径或者绝对路径
1 | /* |
【注意:相对路径参考的是执行node命令的路径;绝对路径是以当前node命令执行的js文件所在目录的路径。】
- 文件中的相对路径,是node命令执行时所在的目录为参考;
- 在使用相对路径时要注意这一点;
- 绝对路径是以当前node命令执行的文件所在的目录;
- 以/开头的也是绝对路径;/1级目录/ 路径以1级目录为参考;
使用:
path.join("1级目录","2级目录","3级目录","文件名+后缀");
-拼接路径;
- window中使用的\,在字符串中使用时需要两个斜杠;有转义的效果;也支持/,
- Linux和mac中使用/,不支持\,
path.join("1级目录","2级目录","3级目录","文件名+后缀")
会根据系统不同使用不同的斜杠;- 支持dirname读取当前目录,支持../跳到上一级目录;`path.join(dirname,”../其他目录/文件名+后缀”)`
其他方法:
path.basename(path[, ext])
-返回文件名+后缀path.dirname(path)
-返回路径的目录名path.extname(path)
-获取路径的扩展名(后缀)
1 | path.basename(path[, ext]) |
http模块-创建服务器
var http = require("http");
模块导入;
创建服务器的基本步骤
- 导入http模块:
var http = require("http");
- 创建服务器:
var server = http.createServer();
- 客户端请求事件:
server.on("request", function(request,response) {});
- 给服务器注册request事件,只要服务器接收到了客户端的请求,就会触发request事件
- request事件有两个参数,request表示请求对象,可以获取所有与请求相关的信息,
- response是响应对象,可以获取所有与响应相关的信息。
- 启动服务器,监听端口:
server.listen(9999, function(){"执行给自己看的代码"});
- 服务器监听的端口范围为:1-65535之间,
- 推荐使用3000以上的端口,因为3000以下的端口一般留给系统使用
- 6000,6665,6666,6667,6668,6669特殊端口,也不能使用;
1 | //1. http模块是nodejs提供的核心的模块, 需要先导入 |
request对象详解
- request.headers: 所有的请求头信息
- request.rawHeaders:所有的请求头信息(数组的方式)
- request.method:请求的方式
- request.url:请求的地址
- 注意点:
- 在发送请求的时候,可能会出现两次请求的情况,这是因为谷歌浏览器会自动增加一个favicon.ico的请求。
- 小结:request对象中,常用的就是method和url两个参数
response对象详解
response响应对象
- response.write(data);-响应给客户端的数据;
- 参数data可以是字符串,也可以是buffer对象;
- 可以有多个response.write(data);会按顺序自动拼接;
- response.end([data]);-表示本次响应结束;
- 必须设置响应结束;否则客户端会默认服务器响应没有结束;
- 参数data,可以不传递,可以是字符串,也可以是buffer对象;
- 设置data参数,解析为先调用了response.write(data);在调用了response.end();
- response.setHeader(“content-type”, “text/html;charset=utf-8”);-设置响应头;让浏览器以utf-8解析;
- response.statusCode=404;-设置响应http状态码;
- response.statusMessage=””;-设置响应状态信息;在设置了response.statusCode;会自动生成响应状态信息;一般不用设置;
- response..writeHead(404, [“响应状态信息”], {“content-type”:’text/html;charset=utf-8’,});-上面三个属性合写;
【注意:必须先设置响应头,才能设置响应;也会发生中文会乱码;】
request有两个事件
接收客户端发送的post数据时触发;
data事件
- post数据大小没有限制,数据可能会分成多段发送;
- chunk事件对象,chunk表示每次接收数据,可以用一个空串,将多次数据拼接起来;
end事件
- 在所有的post数据接收完触发,此时可以读取到拼接起来的字符串数据;
1 | // 接受POST参数 |
mime模块-第三方模块
MIME(Multipurpose Internet Mail Extensions)多用途Internet邮件扩展类型 是一种表示文档性质和格式的标准化方式
浏览器通常使用MIME类型(而不是文件扩展名)来确定如何处理文档;因此服务器将正确的MIME类型附加到响应对象的头部是非常重要的
专门用于设置响应头Content-Type的值;可以根据文件的类型(文件的后缀)返回对应的mime类型;
如果没有设置响应头,浏览器回去自动识别文件对应的类型;一般会有问题;
mime.getType(url);
- 根据url参数返回对应的mime类型;
- 参数可以是文件的后缀字符串(”css”),也可以是带有路径的文件名,自动识别文件后缀;
mime.getExtension('text/plain');
- 根据对应的mime类型返回对应的文件类型(文件的后缀);
npm
npm的组成部分:
- npm网站
- 注册表(registry)包的仓库;
- 命令行工具 (CLI)
npm的基本使用
初始化包
npm init;
-这个命令用于初始化一个包,创建一个package.json
文件,name不能有中文;
npm init -y;
-快速的初始化一个包, 不能是中文名;
规范使用npm,都应该初始化包,我们的项目都应该先执行npm init
安装包
npm install 包名
; //安装指定的包名的最新版本到项目中
npm install 包名@版本号
; //安装指定包的指定版本
npm i 包名
; //简写
本地安装和全局安装
本地安装
- js文件中使用,通过导入加载到文件中;是npm install的默认安装行为;
- npm install 包名;
- 本地安装,会把npm包安装到当前项目的node_modules文件中,作为项目的依赖
全局安装
- 如果想要在命令行中使用命令,则需要通过全局安装;可以在任何目录的命令使用;
npm install -g 包名
;- 全局安装,会把npm包安装到
C:\Users\用户名\AppData\Roaming\npm目录下
,作为命令行工具使用
卸载包
npm uninstall 包名
; //卸载已经安装的包
也可以手动删除;不推荐,package.json文件中的依赖不会删除;
更新包
npm update 包名
更新包
有package.json文件可以直接使用 npm update
它会先到远程仓库查询最新版本,然后查询本地版本。如果本地版本不存在,或者远程版本较新,就会安装。
强制重新安装包
npm install 包名 --force
有package.json文件可以直接使用 npm install --force
一个模块不管是否安装过,npm 都要强制重新安装,可以使用-f或–force参数。
npm清除缓存
在某些情况下,安装失败或者其他操作的时候需要清除缓存;
npm cache clean --force
package.json文件
package.json文件,包(项目)描述文件,用来管理组织一个包(项目),它是一个纯JSON格式的。
作用:描述当前项目(包)的信息,描述当前包(项目)的依赖项;
- 作为一个标准的包,必须要有
package.json
文件进行描述 - 一个项目的node_modules目录通常都会很大,不用拷贝node_modules目录,可以通过
package.json
文件使用npm install
来安装文件中的依赖包 - 命令:
npm install
直接安装项目所有的依赖项(包括开发依赖项) - 命令:
npm i --production
安装项目依赖项(不包括开发依赖)
1 | { |
开发依赖包安装
devDependencies:开发依赖包配置
命令:npm install --save-dev
文档中会有安装方法;
npm下载加速
淘宝镜像:将安装源配置到淘宝镜像地址;不推荐使用;
1 | # 设置淘宝镜像 |
cnpm:安装cnpm命令,使用淘宝镜像下载;
1 | 命令:npm install cnpm -g |
nrm:npm registry manager(npm仓库地址管理工具)
;专门用于切换安装源地址;
1 | # 安装: |
nodemon模块-自动重启-第三方模块
作用:监视到js文件修改后,自动重启node程序
安装:npm i -g nodemon
使用:nodemon 文件.js 运行node程序
npm发布自己的包
- 注册npmjs账号
- 创建项目文件夹
- 创建package.json文件
- 创建index.js实现基本功能
- 创建README.md,描述项目
- npm publish发布
需要通过 npm login
进行登录
需要使用 nrm use npm
把仓库地址切换回npm
版本号一定要改,才能修改后再次上传;
创建自己的cli程序
- 创建要给文件夹
npm init
初始化- 在文件夹中创建一个index.js,这个js文件就是使用命令要执行 的文件
- 在
package.json
中配置bin,在bin中添加一个属性,这个属性就是最终在命令行中使用的命令名称
1 | bin: { |
在JS文件的头部加上#! node
#! node
表示node中输入命令的时候,执行了bin中命令执行的js文件 hcc-cool
==> node hcc-cool.js
console.log("你的第一个命令行程序已经完成了");
- 相对路径是以node命令窗口所在路径为参考;
- 绝对路径以被执行的文件所在的路径为参考;
url模块
用于URL处理与解析;通过url拿到的查询参数都是字符串格式;
url.parse(url)
方法会解析一个url字符串并返回一个URL对象。
- 如果在url参数后,传递一个布尔类型;会调用querystring来解析
URL.query
字符串成为一个对象;
querystring模块 - 查询字符串
用于解析与格式化 URL 查询字符串;只在专门处理查询字符串时使用
querystring.parse(str)
-解析str字符串成为一个对象。
1 | //查询字符串 'foo=bar&abc=xyz&abc=123' 被解析成: |
Node.js 模板引擎
art-template 模板引擎
安装命令:
1 | npm install art-template |
如果一个模版,需要渲染很多次,那么可以使用complie方法;
1 | // 基于模板路径渲染模板 |
其他模板引擎
- ejs
1 | npm install ejs |
1 | // ejs 示例: |
- underscore
1 | npm install underscore |
1 | // underscore 示例: |
- jade/pug
1 | npm install pug |
Node.js模块化
- 在nodejs中,引用由模块组成,nodejs中采用commonJS模块规范。
- 一个js文件就是一个模块;
- 每个模块都是一个独立的作用域,在这个而文件中定义的变量、函数、对象都是私有的,对其他文件不可见。
node.js模块分类
- node.js的核心模块:核心模块只需要引入就可以使用;
1 | var fs = require("fs"); |
- npm安装的第三方模块:通过npm命令下载的包;下载完成后引入使用;
1 | npm install mime |
1 | var mime = require("mime"); |
- 自定义模块.js:自己开发的js文件;通过module.exports导出;
1 | /* |
模块的导出
在模块内部提供了module对象,module.exports对外导出的接口;这个属性指向一个空对象;其他模块加载的就是module.exports;
1 | var a = 1; |
【module.exports对外导出的接口;所以当其中一个引用地址改变之后,module对象操作的就不是对外导出的数据了;】
复杂对象赋值地址引用
当一个变量a指向一个复杂数据类型时,引用的是复杂数据类型的存储地址;
另一个变量b被a变量赋值(赋值的是引用地址),b也指向这个复杂数据类型的存储地址;
所以两个变量只要有一个赋值被改变的时候,都不在产生关联;不在指向同一个地址;
1 | // 复杂对象obj的地址赋值给另外一个变量a后,即使再次修改obj地址,也不会影响a; |
在每个模块内部还提供了一个变量exports;变量exports也指向了module.exports的空对象地址;由于是复杂类型,所以都指向了同一个复杂类型的地址;所以在两个都没有改变指向地址时,添加属性的操作是等价的;如果任意一个改变了地址引用,那么这两个就不在指向同一地址了,使用时一定要注意;
1 | console.log(module.exports === exports); //true |
【注意:极度不推荐使用exports来操作;】
模块的导入
- 通过require(“”)来加载模块;核心模块直接加载使用;
1 | var fs = require("fs"); |
- 第三方模块,通过npm命令安装后,在加载;
- 先在当前目录查找node_modules目录;如果有,则去该目录中找package.json文件;
- 在package.json文件中查找main属性,找到main对应的路径文件;
- 如果没有查找到main属性,或者main属性对应的路径没有文件,或者没有package.json文件;
- 则查找index.js index.node index.json文件,如果还没有查找到,就会向上级目录查找node_modules目录;
- 如果上级有node_modules目录,则重复之前的过程,如果没有找到node_modules目录;
- 就会一直向上级查找,一直到根目录,如果还没有就报错:can not find module xxx
1 | npm install mime |
1 | var mime = require("mime"); |
- 自定义模块,一定要使用相对路径”./../“;,否则会被识别为第三方模块;
1 | /* |
Express框架
基于node平台的一款快速、开放、极简的 web 开发框架;
Express框架的基本使用
安装命令:
1 | npm install express |
基本使用:
1 | //创建一个Express应用,并返回,相当于创建了服务器; |
注册路由的三种方式
1 | //只识别url的页面请求(根据监听类型),可以用?带参数; |
处理静态资源
1 | /* |
request的常用属性和方法
1 | //获取请求页面地址的第一个/和?之间的内容 |
response的常用属性和方法
1 | //响应给客户端的数据;自动设置响应头,自动调用end方法; |
Express使用模板引擎
Express安装
1 | npm install express |
给express绑定一个模版引擎
1 | //给express设置模版引擎 |
通过res.render()
渲染模版引擎
1 | //参数1; 模版文件的路径,相对路径,会去views目录下查找;也可以是用绝对路径; |
关于模版引擎的配置
1 | //模版文件默认去aa目录下查找 默认值:views |
中间件
中间件(Middleware) 是一个函数,它可以访问请求对象request,响应对象response,可以通过next参数将中间件传递到下一个中间件;
中间件的功能包括:
- 执行任何代码。
- 修改请求和响应对象。
- 可以结束整个响应
res.send() res.end() res.sendFile()
- 调用堆栈中的下一个中间件。
定义一个中间件
1 | //添加一个中间件 |
body-parser中间件的使用
- 获取get请求的参数:
req.query
- 获取post请求的参数:
req.body
,但是需要借助body-parser
中间件;
安装:
1 | npm install body-parser |
使用:
1 | //1. 导入body-parser中间件 |
【注意:中间件是有执行顺序的】
实现自己的body-parser中间件
1 | //bodyParser.json(); 返回一个函数(中间件),会处理json数据 |
外置路由的使用
目的:将路由封装到一个独立的路由模块中,有利于代码的封装和模块化;
1 | /* |
1 | /* |
【注意:可以定义多个外置路由,通过app.js来导入,注册多个路由挂载在app实例上,不会互相干扰;】