1、介绍
NodeJs是一个JavaScript运行环境,让Js可以开发后端程序,实现几乎其他后端语言实现的所有功能
NodeJs是基于V8引擎,本身就是用于Chrome浏览器的Js解释部分,只是把V8搬到了服务器上了
NodeJs的首要目标是提供一种简单、用于创建高性能服务器及可在服务器中运行的各种应用程序的开发工具
NodeJs语言和Java非常像,是跨平台的
2、什么是V8 JavaScript脚本语言
是一种被V8引擎解释并执行的脚本语言
是一种使用C++开发的高性能JavaScript引擎,该引擎并不局限于在浏览器中运行,NodeJs将其转用在了服务器上,并附加不同用途的API
3、现在服务器端语言存在的问题
- 会为每一个客户端连接创建一个新的线程,而每一个线程大约会消耗2MB内存,想要让Web应用程序支持更多的用户,就需要增加服务器的数量,而硬件成本就随之上升了
4、优势
完全是Js语法【打破了Js只能在浏览器中运行的局面,使得前后端的编程环境统一,大大的降低了开发成本】
NodeJs有超强的高并发能力,它不为每一个客户连接创建一个新的线程,而是仅仅使用一个线程,当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让NodeJs程序宏观上也是并行的
可以实现高性能的服务器
开发周期短、开发成本低、学习成本低【花最小的硬件成本,追求更高的并发,更高的处理性能】
5、NodeJs可以做什么
6、如何实现一个简易的服务器
(1)引入HTTP模块
(2)创建服务器
(3)运行程序 node xxx.js
let http = require('http');
// -> 创建一个服务器,回调函数表示接收到请求之后做的事情
http.createServer((req, res) => {
// req -> 请求【获取url信息 - req.url】
// res -> 响应【浏览器返回的信息】
// 发送HTTP头部 - HTTP状态值:200 OK - 文件类型是http - 字符集是utf8
res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
// 发送响应数据
res.end("123 | 456"); // -> end方法使web服务器停止处理脚本并返回当前结果
}).listen(8090); // -> 使用listen方法监听端口
7、url模块
url.parse() 解析URL
url.format(urlObject) ->
是上面url.parse()操作的逆向操作
url.resolve(from,to) ->
添加或者替换地址
let http = require('http');
let url = require('url');
http.createServer((req, res) => {
if (req.url != '/favicon.ico') {
// -> 加true变为Object格式的
let { pathname, query } = url.parse(req.url, true);
console.log(pathname, query);
}
}).listen(8091);
8、Node自启动工具supervisor
作用:
修改代码自动重启服务器
安装:
npm i supervisor -g
代替:
使用supervisor代替node命令启动应用
9、模块化
9.1 什么是CommonJs
是为了弥补JavaScript没有标准的缺陷,提供了一些可以让我们使用的模块的API(标准库)
CommonJs就是模块化的标准,而NodeJs就是模块化的实现
9.2 NodeJs中的模块化
- NodeJs中模块分为两类:
核心模块【直接引入使用】
和文件模块【自定义模块,用户自己写的】
9.3 CommonJs中自定义模块的规定
可以把公共的功能抽离成单独的一个JS文件作为一个模块,默认情况下这个模块里的方法和属性外部是没有办法直接访问的,如果想让外部进行访问,可以通过exports或者module.exports暴露属性或者方法
在需要使用这些模块的文件中,通过require的方式引入这些模块,这样就可以使用模块中暴露的属性或者方法了
var tools = require('./tools'); // -> 可以省略js后缀名
var foo = require('foo');// -> foo默认在目录下面没有的话,nodeJs会在node_modules里面找这个模块
10、npm
10.1 包
- NodeJs中第三方模块由包组成,可以通过包来对一组具有相互依赖关系的模块进行统一管理
完全符合CommonJs规范的包目录一般包含如下文件
(1)package.json 包描述文件
(2)bin 存放可执行二进制文件的目录
(3)lib 存放javascript代码的目录
(4)doc 存放文档的目录
NodeJs中通过
npm
命令来下载第三方模块包
10.2 npm介绍
是世界上最大的开放源代码的生态系统,可以通过npm下载各种各样的包,也就做包管理工具【能解决NodeJs代码部署上的很多问题】,通常会有以下的应用场景:
(1)从npm下载第三方包到本地使用
(2)允许用户将自己编写的包发布到npm供别人使用
10.3 常用的npm命令
npm -v
- 查看npm版本npm install
- 使用npm命令安装模块npm uninstall
- 使用npm命令卸载模块npm list
- 查看当前目录下已安装的node包npm info
- 查看模块的版本npm install xxx@3.1.1
- 指定版本安装
10.4 package.json
10.4.1 作用
- 定义了项目所需要的各种模块,以及项目的配置信息
10.4.2 创建
npm init
10.4.3 dependencies与devDependencies之间的区别
dependencies 【配置当前程序所依赖的其他包】【对应 —save】
devDependencies 【只会下载模块,而不会下载这些模块的测试和文档框架】【对应 —save-dev】
10.5 安装淘宝镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
11、fs
11.1 fs.stat - 检测是文件还是目录
var fs = require('fs');
fs.stat('README.md', (err, stats) => {
if (err) console.log(err)
else {
console.log(`文件:${stats.isFile()}`);
console.log(`目录:${stats.isDirectory()}`);
}
})
11.2 fs.mkdir - 创建目录
var fs = require('fs');
/**
* @param path -- 将创建的目录路径
* @param mode -- 目录权限(读写权限),默认0777
* @param callback -- 回调,传入异常参数err
*/
fs.mkdir('e:/05/css', (err) => {
if (err) {
console.log(err);
} else {
console.log('创建目录成功');
}
})
11.3 fs.writeFile - 创建写入文件
var fs = require('fs');
/**
* @param filename -- (String) -- 文件名称
* @param data -- (String | Buffer) -- 将要写入的内容,可以是字符串或者是Buffer数据
* @param options -- (Object)
* - encoding -- (String) -- 可选值,默认是utf8;当data是buffer时,该值为ignored
* - mode -- (Number) -- 文件读写权限,默认值438
* - flag -- (String) -- 默认值'w'
* @param callback -- (Function) -- 回调,传递一个异常参数err
*/
fs.writeFile('e:/05/css/1.txt', 'Hello World', 'utf8', err => {
if (err) {
console.log(err);
} else {
console.log('写入成功');
}
})
11.4 fs.appendFile - 追加文件
和writeFile的语法基本相同,只是含义不同
writeFile
覆盖
appendFile
追加
11.5 fs.readFile - 读取文件
var fs = require('fs');
fs.readFile('e:/05/css/1.txt', (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data); // -> 得到的是buffer数据
console.log(data.toString()); // -> 得到的是字符串内容
}
})
11.6 fs.readdir - 读取目录
var fs = require('fs');
fs.readdir('e:/Node', (err, files) => {
if (err) {
console.log(err);
} else {
console.log(files); // -> 返回的是一个数组,把目录下面的文件和文件夹都获取到
}
});
11.7 fs.rename - 重命名
var fs = require('fs');
// 1.重命名
fs.rename('e:/05/css/1.txt', 'e:/05/css/2.txt', (err) => {
if (err) {
console.log(err);
} else {
console.log('重命名成功');
}
});
// 2.剪切
fs.rename('e:/05/style.css', 'e:/05/css/style.css', (err) => {
if (err) {
console.log(err);
} else {
console.log('剪切成功');
}
});
11.8 fs.rmdir - 删除目录
var fs = require('fs');
fs.rmdir('e:/05', (err) => {
if (err) {
console.log(err);
} else {
console.log('目录删除成功');
}
});
11.9 fs.unlink - 删除文件
var fs = require('fs');
fs.unlink('e:/05/1.txt', (err) => {
if (err) {
console.log(err);
} else {
console.log('文件删除成功');
}
});
11.10 fs.createReadStream - 从文件流中读取数据
var fs = require('fs');
var readStream = fs.createReadStream('./data.json');
// -> 定义变量进行保存数据
var str = '';
// -> 通过on去监听数据【data (一块块接收),end (接收完成),error (出错)】
readStream.on('data', (chunk) => {
str += chunk;
});
readStream.on('end', () => {
console.log(str);
});
readStream.on('error', (err) => {
console.log(err);
});
11.11 fs.createWriteStream - 写入文件
var fs = require('fs');
// -> 需要写入的数据
var data = 'Hello Stream';
// -> 创建一个可写入流,写入到文件中
var writeStream = fs.createWriteStream('output.txt');
// -> 使用UTF8编码写入数据
writeStream.write(data, 'UTF8');
// -> 标记文件末尾
writeStream.end();
// -> 处理流事件
// -> 所有数据已被写入到底层系统时触发
writeStream.on('finish', () => {
console.log('写入完成');
});
writeStream.on('error', (err) => {
console.log(err);
});
11.12 管道流
var fs = require('fs');
// -> 创建一个可读流
var readerStream = fs.createReadStream('input.txt');
// -> 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');
// -> 管道读写操作 - 【读取input.txt中的内容,并将内容写入到output.txt中】
readerStream.pipe(writerStream);
12、静态web服务器
12.1 介绍
web服务器一般指网站服务器,可以向浏览器等web客户端提供文档,也可以放置网站文件、数据文件
目前世界上最主流的三个web服务器是
Apache
、Nginx
、IIS
12.2 服务器的实现
// -> 目录结构
/*
model
|-- getMimefromfile.js
|-- mime.json
static
|-- css
|-- js
|--index.html
*/
// -> 处理content-type类型
exports.getMime = function (fs, extname) {
var data = fs.readFileSync('./mime.json');
var Mimes = JSON.parse(data.toString());
return Mimes[extname] || 'text/html';
}
var http = require('http'),
fs = require('fs'),
path = require('path'),
url = require('url');
var mimeModel = require('./model/getMimefromfile.js');
http.createServer((req, res) => {
// -> 指定content-type
res.writeHead(200, { "Content-Type": "text/html;charset='utf-8'" });
var pathname = url.parse(req.url).pathname;
// -> 默认加载首页
if (pathname == '/') {
pathname = '/index.html';
}
// -> 获取文件的后缀名
var extname = path.extname(pathname);
// -> 过滤请求
if (pathname != '/favicon.ico') {
// -> 文件操作,获取static下面的文件
fs.readFile('static' + pathname, (err, data) => {
if (err) {
console.log(err);
} else {
var mime = mimeModel.getMime(fs,extname);
res.writeHead(200, { "Content-Type": "" + mime + ";charset='utf-8'" });
res.write(data);
res.end();
}
})
}
}).listen(8010);
13、处理异步的方法
13.1 回调函数
function getData(callback) {
var result = '';
setTimeout(() => {
result = 'this is data';
callback(result);
}, 500);
}
getData((data) => {
console.log(data);
});
13.2 events模块
nodejs是单线程单进程的应用程序,但是通过事件和回调支持并发,所以性能非常的高
nodejs的每一个API都是异步的,并作为一个独立的线程运行,使用异步函数调用,并处理并发
nodejs有多个内置的事件,我们可以通过引入events模块,并通过实例化EventEmitter类来绑定和监听事件
let events = require('events');
let evnetEmitter = new events.EventEmitter();
// -> 广播和接收广播
evnetEmitter.on('to_parent', (data) => {
console.log('接收了广播');
console.log(data);
});
setTimeout(() => {
console.log('广播开始');
evnetEmitter.emit('to_parent','Jerry');
}, 1000);
14、NodeJs路由
官方解释:
是由一个URI和特定的HTTP方法组成的,涉及到应用如何响应客户端对某个网站节点的访问
非官方解释:
就是针对不同请求的URL,处理不同的业务逻辑
15、ejs模板
<h3>123</h3>
<h2><%=msg%></h2>
<ul>
<%for(var i =0;i < list.length; i++){%>
<li><%=list[i]%></li>
<%}%>
</ul>
const http = require('http'),
fs = require('fs'),
url = require('url'),
ejs = require('ejs');
http.createServer((req, res) => {
res.writeHead(200, {"Content-type":"text/html;charset='utf8'"})
let { pathname } = url.parse(req.url, true);
if (pathname === '/login') {
// -> 第一个参数:模板url路径
// -> 第二个参数:数据
// -> 第三个参数:callback
ejs.renderFile('views/login.ejs', {
msg:'Hello Ejs',
list:["111","222","333"]
}, (err, data) => {
res.end(data);
})
}
}).listen(3000)
16、获取get、post传值
const http = require('http'),
url = require('url'),
ejs = require('ejs');
http.createServer((req, res) => {
res.writeHead(200, {"Content-type":"text/html;charset='utf8'"})
let { pathname, query } = url.parse(req.url, true);
if (pathname === '/doLogin') {
console.log(query); // -> GET { username: '123', password: '123' }
// POST
var data = '';
req.on('data', (chunk) => {
data += chunk;
})
req.on('end', () => {
console.log(data) // username=12345556&password=6789898
})
ejs.renderFile('views/index.ejs', {}, (err, data) => {
res.end(data);
})
}
}).listen(3003)
17、路由封装
- 路由指的就是针对不同的url请求,处理不同的业务逻辑
var url=require('url');
//封装方法改变res 绑定res.send()
function changeRes(res){
res.send=function(data){
res.writeHead(200,{"Content-Type":"text/html;charset='utf-8'"});
res.end(data);
}
}
//暴露的模块
var Server=function(){
var G=this; /*全局变量*/
//处理get和post请求
this._get={};
this._post={};
var app=function(req,res){
changeRes(res);
//获取路由
var pathname=url.parse(req.url).pathname;
if(!pathname.endsWith('/')){
pathname=pathname+'/';
}
//获取请求的方式 get post
var method=req.method.toLowerCase();
if(G['_'+method][pathname]){
if(method=='post'){ /*执行post请求*/
var postStr='';
req.on('data',function(chunk){
postStr+=chunk;
})
req.on('end',function(err,chunk) {
req.body=postStr; /*表示拿到post的值*/
G['_'+method][pathname](req,res); /*执行方法*/
})
}else{ /*执行get请求*/
G['_'+method][pathname](req,res); /*执行方法*/
}
}else{
res.end('no router');
}
}
app.get=function(string,callback){
if(!string.endsWith('/')){
string=string+'/';
}
if(!string.startsWith('/')){
string='/'+string;
}
G._get[string]=callback;
}
app.post=function(string,callback){
if(!string.endsWith('/')){
string=string+'/';
}
if(!string.startsWith('/')){
string='/'+string;
}
G._post[string]=callback;
}
return app;
}
module.exports=Server();