node是一个让js运行在服务器端的开发平台,使得javascript的触角衍生到了服务器端,与python、ruby、php平起平坐。
但node有些不同:
1.其并非一门独立语言,与php、jsp的“既是语言,也是平台”不同,node.js的使用javascript进行编程,运行于javascript引擎之上。
2.与php,jsp相比,node跳过了apache、nginx、iis等http服务器,他自己不需要建设在任何服务器软件之上.
node的许多设计理念与经典架构(lamp)有着很大的不同,可以提供强大的伸缩能力
Node三个极限特点:
1.单线程
node只有一个线程,所有的用户都是用这一根线程完成服务.
2.非阻塞I/O
在阻塞模式下,一个线程为所有用户提供服务
3.事件驱动
聪明的node面对i/o的时候,并不会傻傻地等待当个需要处理i/o的线程任务完毕之后在处理下一个,它会将需要处理i/o的任务存放到内部的一种栈中,先完成便于完成的服务,再回头依次解决各种任务.
Node.js底层是C++(v8同样是使用C++)编写.对于其底层代码中,有接近半数都是用是事件队列、回调函数队列的构建。用事件驱动来完成服务器的任务调度。
node适合做什么开发?
善于I/O,不善于计算.因为node最擅长的就是任务调度,如果你的业务有很多的cpu计算,实际上也相当于这个计算阻塞了这个单线程,就不适合Node开发.
当应用程序需要处理大量的并发I/O,而向客户端发出相应之前,应用程序内部并不需要进行非常复杂的处理的时候,node非常适合.node也同样非常适合与websocket配合,开发长连接的实时交互应用程序
helloworld
注意:
- node是服务器程序,书写的js语句,都将运行在服务器之上。 返回给客户的,都是已经处理好的纯html。无法查看网页源代码
- node可以直接运行js文件(直接将js文件拖入浏览器中是无法识别的)
- node没有web容器! 无法通过node指令 运行html文件。但,我们可以使用fs模块,通过它的readfile()方法,读取html文件,并在回调函数中将其渲染
由于该方法属于http模块,使用前需要引入http模块(var http= require(“http”) )
本质:
http.createServer([requestListener])
接收参数:
requestListener 请求处理函数,自动添加到 request 事件,函数传递两个参数:
req 请求对象,想知道req有哪些属性,可以查看 “http.request 属性整合”。
res 响应对象 ,收到请求后要做出的响应。想知道res有哪些属性,可以查看 “http.response属性整合”。
//引包 : 引用自己的某个特殊功能
var http = require("http");
//创建服务器,参数是一个回调函数,表示如果有请求进来,需要完成什么
var server = http.createServer(function(req,res){
//req 表示请求,request;res表示响应,response
//设置HTTP头部,状态码为200,文件类型是html,字符集是utf8
res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
// res.end("这是我的第一个node页面");
//每一个回调函数,都需要一个end函数
//否则浏览器会持续等待浏览器发送end中的内容,直到访问超时,所以即便end中给一个空,也不要不给.
res.end("我买了一部华为P"+(8+10+12)+"pro");
});
//运行服务器 监听3000端口号
server.listen(3000,"127.0.0.1")
//设置完毕之后,使用 node + "文件所在目录" 打开
上述代码中也可以通过:res.setHeader("Content-Type","text/html;charset=utf-8")直接设置编码格式
就不需要在writeHeader中重复编写了
node的路由:
说起来node的路由很有意(麻)思(烦)。在请求地址根后斜杠后添加上一串字符,node会截取该字符,并用于if的判断上,如果匹配成功,则显示某个页面,否则显示其余页面,或提示访问者无此页面。
var http = require("http");
//fs包用于文件读取
var fs = require("fs");
var server = http.createServer(function(req,res){
//使用fs读取上层目录的index文件
if (req.url == "/haha") {
//当截取的字符串匹配‘haha’成功时
//fs会读取circle.html
fs.readFile("./circle.html",function(err,data){
//**注意 : 此处千万不要写成了 data.writeHead(......)**
//调用writehead方法的对象还是 http.createServer实例的res对象
res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
//data的显示,是通过res.end(对象名)
res.end(data)
})
}else if(req.url == "/index"){
//url等于index时,显示index页面
fs.readFile("../index.html",function(err,data){
res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
res.end(data)
})
}else{
//并未匹配成功 ,显示 "没有此页面"
res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
res.end("没有这个页面")
}
});
//监听地址与端口
server.listen(3000,"127.0.0.1")
当需要显示单个图片可以写成
if(req.url == "/0.jpg"){
fs.readFile("./0.jpg",function(err,data){
//contenr需要改变
res.writeHead(200,{"Content-type":"image/jpg"});
res.end(data)
})
}
http模块
在前几个例子中,已经介绍了http模块的部分特点.下面再添加几个视频中提及到的知识点:
res.writeHead()
//该格式可以识别HTML结构,编码格式是UTF-8 >
res.writeHead(200,{‘Content-Type’:‘text/html;charset=UTF8’});
//该格式不可以识别HTML结构,显示的是纯文本
res.writeHead(200,{‘Content-Type’:‘text/plain;charset=UTF8’});
//该格式识别图片
res.writeHead(200,{‘Content-Type’:‘image/jpg;charset=UTF8’});
//该格式识别样式
res.writeHead(200,{‘Content-Type’:‘text/css;charset=UFT8’});
------我是分割线---------------
res.write();
用来输出相应的返回数据
res.end()
res.end()需要时最后一个执行的方法,否则会报错,且参数希望是一个字符串或者缓存区内容,不可以执行如:
res.end(2+3+4) //报错
希望写成:
res.end((2+3+4).toString())
var http = require('http');
var server = http.createServer(function (req,res) {
if(req.url == "/"){
$msg = "this is index"
}else if(req.url == "/login"){
$msg = "this is login"
}else{
$msg = "404"
}
console.log("服务器接收到了请求"+req.url)
/*因为实际开发中,我们需要返回对应的中文以及对应的的文本格式
* 所以我们需要设置对应的响应头,响应头决定了对应的返回数据的格式以及编码格式
* writeHead:这个方法有两个参数,第一个参数表示对应的编码的状态值,第二个表示对应的设置*/
res.writeHead(200,{'Content-Type':'text/html;charset=UTF8'});
res.write($msg);
res.end('<h1>我是h1标签</h1>');
});
server.listen(3000,'127.0.0.1');
模块的三种分类:
- 内置/核心模块:http服务、fs文件操作、url路径、path路径处理、os操作系统
- 第三方模块
- 自定义模块
自定义模块需要遵守的约定—CommonJs
- 一个文件就是一个模块
- 通过exports和modul.exports来到出模块中的成员(需要生命模块中哪些功能可以使用)
- 模块需要通过require加载
语法
###步骤1:导出成员(声明模块/文件中哪些方法可以被外部调用)
//写法1
exports.属性/方法名 = 功能
//写法2
module.exports.属性/方法名 = 变量名;
###步骤2:外部引入使用
var 对象 = require("路径及文件名");
对象.属性或方法名;
接下来我们写个demo:
//b.js 在b.js中定义模块
//step1 :定义
var add = () =>{ console.log("add: 此方法可以执行增加操作")}
var del = () =>{ console.log("del: 此方法可以执行删除操作")}
var edit = () =>{ console.log("edit: 此方法可以执行编辑操作")}
var select = () =>{ console.log("select: 此方法可以执行查询操作")}
//step2 :导出 (语法: exports/module.exports.成员名/方法名 = 值)
exports.add = add;
module.exports.del = del;
exports.edit = edit;
module.exports.select = select;
//a.js a.js负责引入
let moduleb = require("/b");//注意,这里的后缀不需要写上
console.log(module);
//以下是打印出来的东西
//C:\Users\LYn\Documents\HBuilderProjects\node学习\js>node a.js
//{ add: [Function: add],
// del: [Function: del],
// edit: [Function: edit],
// select: [Function: select] }
moduleb.add();
//打印出(实际就是b.js中箭头函数的函数体): 此方法可以执行增加操作
OS模块学习:
let myos = require("os");
console.log('hello' +myos.EOL+ 'itcast');
console.log("主机名"+myos.hostname());
console.log("操作系统名"+myos.type());
console.log("操作系统平台" +myos.platform);
console.log("内存总量" +myos.totalmem+"字节");
console.log("空闲内存" +myos.freemem() +"字节");
//打印出
hello
itcast //此处hello 与 itcast换行的原因是 有myos.EOL 这是个换行
主机名DESKTOP-90M26FL
操作系统名Windows_NT
操作系统平台win32
内存总量8484425728字节
空闲内存4014014464字节
path模块学习
dirname 表示回退一层目录
basename 表示到最深层目录(或文件)
let mypath = require("path");
var testpath = "c:/app/view/index.html"
console.log(mypath.basename(testpath)); //输出index.html
testpath = mypath.dirname(testpath);
console.log(testpath); //输出 c:/app/view
testpath = mypath.dirname(testpath)
console.log(testpath); //输出c:/app
url模块学习
let myurl = require("url");
var data = 'http://itcast.cn?name=张三&age=18';
console.log(data);
console.log(myurl.parse(data));
console.log(myurl.parse(data,true)); //添加上true参数可以将 query属性的值转变为对象
console.log(myurl.parse(data).search); //属性同样支持点号运算符,这是个对象
fs模块读写操作
var fs = reuqire("fs"); //引入模块
fs.writeFile("./a.txt","你好,未来",(err)=>{
if(err){console.log(err);return;}
console.log("success");
})
fs的 writeFile方法的三个参数,分别是:对应的文件、需要输入的文字、回调函数。
//写法一
var fs = reuqire("fs"); //引入模块
fs.readFile("./a.txt",(err,data)=>{
if(err){console.log(err);return;}
console.log(data);
//这里的内容我们无法识别 是一个buffer对象(缓冲区)
//通过Buffer对象 Buffer.toString()转换为字符显示
console.log(data.toString()); //成功打印
})
//写法二 咋子readfile函数中增加一个字符参数
fs.readFile("./a.txt","utf8",(err,data)=>{
if(err){console.log(err);return;}
console.log(data);
})
fs的 readFile方法的回调函数中第一个参数是err,第二个是获取的数据
Demo 我们接下来会使用node搭建一个小的服务器
目录解构
处理静态资源的主要代码(js):
html页面只是一些文字,并不重要,这里我们应该关注的点是:该如何处理好node的路由
//app.js
var http = require("http"); //引入http模块
var fs = require("fs");//引入fs模块 用于文件读取
var httpServer = http.createServer(); //创建服务
httpServer.on("request",(req,res)=>{
//为了判断访问什么文件,需要获取当前路径
var currentUrl = req.url
// res.setHeader("Content-Type","charset=utf-8")
//请求"/"加载留言板列表
if(currentUrl =="/"){
fs.readFile("./view/index.html","utf8",(err,data)=>{
if(err) res.end("404 not found");
res.write(data);
res.end();
})
}else if(currentUrl =="/add"){
fs.readFile("./view/add.html","utf8",(err,data)=>{
if(err) res.end("404 not found");
res.write(data);
res.end();
})
//判断是否能与'/public'匹配,若成功,则渲染出来
}else if(currentUrl.indexOf("/public") === 0){
console.log(currentUrl);
fs.readFile('./'+currentUrl,"utf8",(err,data)=>{
if(err) res.end("404 not found");
res.write(data);
res.end();
})
}else{
console.log(currentUrl);
fs.readFile("./view/404.html","utf8",(err,data)=>{
if(err) res.end("404 not found");
res.write(data);
res.end();
})
}
//请求"/add"加载留言板添加
//检测静态资源并响应
//否则404
})
//4.启动服务
httpServer.listen(8000,()=>{
console.log("启动成功,即将访问:http://localhost:8000");
})
//-----404.html
最终结果
var http = require("http"); //引入http模块
var url = require("url"); //引入url模块 处理路径
var fs = require("fs");//引入fs模块 用于文件读取
var querystring = require("querystring");//引入fs模块 用于文件读取
var httpServer = http.createServer(); //创建服务
var msg = [
{name:"张三",content:"hello,我叫张三"},
{name:"李四",content:"hello,我叫李四"},
{name:"王五",content:"hello,我叫王五"}
]
httpServer.on("request",(req,res)=>{
//为了判断访问什么文件,需要获取当前路径
var currentUrl = req.url
// res.setHeader("Content-Type","charset=utf-8")
//请求"/"加载留言板列表
if(currentUrl =="/"){
fs.readFile("./view/index.html","utf8",(err,data)=>{
var str = "";
msg.forEach((item=>{
str +=`<li>姓名:${item.name} .他说: ${item.content}</li>`
}))
var data = data.replace("li填充",str)
if(err) res.end("404 not found");
res.write(data);
res.end();
})
//请求"/add"加载留言板添加
}else if(currentUrl =="/add"){
fs.readFile("./view/add.html","utf8",(err,data)=>{
if(err) res.end("404 not found");
res.write(data);
res.end();
})
//判断是否能与'/public'匹配,若成功,则渲染出来
}else if(currentUrl.indexOf("/public") === 0){
console.log(currentUrl);
fs.readFile('./'+currentUrl,"utf8",(err,data)=>{
if(err) res.end("404 not found");
res.write(data);
res.end();
})
}else if(currentUrl.indexOf('/doadd') == 0){
console.log(req);
//数据处理
//首先判断请求的方式 可使用req.request方法
if(req.method == "POST"){
//注意 : 使用post请求时,数据量可能很庞大,因此分片获取
//说明 : data事件 -- 数据正在传输中, end事件 -- 数据传输完毕
var postData = "";
req.on("data",(chunk)=>{
postData +=chunk;
});
req.on("end",function(){
paramobj = querystring.parse(postData);
msg.push(paramobj)
//3.跳转
res.statusCode = 302;
res.setHeader("Location","/");
res.end();
});
}else{
//1.接收数据
var paramobj = url.parse(req.url,true).query
// console.log(paramobj); 先打印观察数据格式
//2.入库(此时没有数据库,我们直接将数据压入msg中
msg.push(paramobj)
//3.跳转
res.statusCode = 302;
res.setHeader("Location","/");
res.end();
}
}else{
console.log(currentUrl);
fs.readFile("./view/404.html","utf8",(err,data)=>{
if(err) res.end("404 not found");
res.write(data);
res.end();
})
}
//检测静态资源并响应
//否则404
})
//4.启动服务
httpServer.listen(8000,()=>{
console.log("启动成功,即将访问:http://localhost:8000");
})
npm指令
-
npm -v :版本查询
-
npm init 项目初始化 ( 生成package.json文件) 注意: .json文件 不可以添加任何注释 否则报错
-
npm install 包名 --save-dev (npm install 包名 -D):注意 安装的包只用于开发环境,不用于生产环境
-
npm install 包名 --save 安装的包需要发布到生产环境的
安装完毕后在你的文件夹中会出现多出一个node_modules的文件夹和package-lock.js的文件,在node_modules文件夹中会有子文件夹为你刚刚安装的包,在package.js中也会出现刚刚安装的文件以及版本号 -
npm ls 查看安装的模块
-
npm help 查看某条命令的详细帮助
Buffer(缓冲区)
1.Buffer的结构和数组很像,操作方法也很类似
2.数组中 不能存储二进制文件,而buffer就是弥补数组的这种缺陷。我们可以将几乎所有的文件类型转变成二进制文件存储,这也显示除buffer的易用
3.使用buffer不需要引入模块,直接使用即可
4.虽然buffer中所有的数据都是二进制,但却是以16进制显示(2进制太长)
buffer中存储的每一个元素的范围是00 - ff 0-255
00000000 - 11111111
计算机 一个 0 或者一个 1 表示一位(bit)
8bit -----> 1byte字节
var str = "today is thursday"
将一个字符串保存到buffer中
var buf = Buffer.from(str);
console.log(buf);
得到
<Buffer 74 6f 64 61 79 20 69 73 20 74 68 75 72 73 64 61 79>
// 这其中的每一个数字都是16进制,虽然说buffer需要存储二进制,但是计算机中 所有进制都会转变成16进制 ,便于保存(2位数,如果是2进制则需要更多位)
注意 :buffer所有构造函数,都不推荐使用 因为很多都已经被废弃,存在着隐患
alloc 方法 : 分配一个大小为 size 字节的新建的Buffer.
使用alloc为变量buf2 分配大小
Buffer 一旦创建, 大小是不可以被改变的,实际上,Buffer是对底层内存的操作
var buf2 = Buffer.alloc(10);
为第 0 位索引赋值为 88
buf2[0] = 88;
console.log(buf2);
注意: 每个索引分配的值 最好是连续的 ,便于查找,对性能有帮助
Buffer.allocUnsafe(size) 创建一个指定大小的buffer,但是buffer中可能含有敏感数据
var buf3 = Buffer.allocUnsafe(10);
console.log(buf3);
注意 与alloc不同的是, allocunsafe只是分配一个连续的10位空间,他并不会将每一位中的数据清除(内存是反复使用的,所以这些遗留的数据是上一次未清除的)
分割线
关于fs 受从操作文件的步骤(这里给出的列子都是同步执行)
// 1.打开文件
fs.openSync(path,flags[,mode])
/* - path 要打开的文件路径
- flags 打开文件需要做的操作的类型
r : 只读
w : 只写
- mode 设置文件的操作权限,一般不传
返回值:
- 该方法会返回一个文件的描述符作为结果, 我们可以通过该描述符来对文件进行各种操作
2.向文件中写入内容
-fd 文件的描述符
-string 要写入的内容
*/
fs.writeSync(td,string[,position[,encoding]])
// td - 写入哪个文件
// string 写入的字符串
// position 从什么位置开始写入
// encoding 编码格式
// 3.保存并关闭文件
EG:
// 导包
var fs = require("fs")
// 打开文件
var file = fs.openSync("hello.txt","w")
// 写入文件
fs.writeSync(file,"今天天气很好");
// 保存并关闭文件,释放内存
fs.closeSync(file);
// 完成
关于fs 受从操作文件的步骤(这里给出的列子都是异步执行)
var fs = reqiure("fs");
fs.open("text.txt","w",function(err,fd){
// 异步读写操作
// 第一个参数 :需要操作的文件名
// 第二个参数 : 写操作
// 第三个参数 : 回调函数
// 回调函数 :err , 打开的文件赋予的变量名(以下操作变量名即可)
if(!err){
fs.write(fd,"执行异步读写操作",function(err){
if(!err){
// 打印提示 写入成功
console.log("成功写入")
}
// 成功写入后 需要关闭文件,释放内存
fs.close(fd,function(err){
if(!err){
console.log("成功关闭")
}
})
})
}
})
// 这一句永远在另外两条console语句之前打印,因为open是异步操作的
console.log("异步执行")
有个悲伤的故事,前面两种方式比较麻烦我们基本不用,下面介绍一个简单读写方式 ?…
var fs = require("fs");
fs.writeFile(file,data[,options],callback) // 异步
fs.writeFileSync(file,data[,options]) // 同步
/*
- file 要写入的文件路径
- data 要写入的数据
- options 选项,可以对写入进行 一段设置
- callback 回调函数
*/
// 这种写法不需要 打开文件,也不需要关闭文件
EG:
fs.writeFile("text.txt","这是我们需要写入的数据",function(err){
if(!err){
console.log("写入成功")
}
})
上述的writefile已经将所有的需要操作进行了封装,因此我们不需要再重复操作