Node_进阶_2
Posted eret9616
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node_进阶_2相关的知识,希望对你有一定的参考价值。
第二天
一、复习:
Node.js开发服务器、数据、路由。本地关心效果,交互。
Node.js实际上是极客开发出的一个小玩具,不是银弹。有着别人不具备的怪异特点:
单线程、非阻塞I/O、事件驱动。 实际上是一个特点。
首先,Node不为每个用户开辟一个线程,所以非常极端的选择了单线程。
单线程,要照顾所有的用户,必须有非阻塞I/O否则一个人的I/O就把别人、自己都阻塞了。
一旦有非阻塞I/O,一个人如果I/O去了,就会放弃CPU的使用权,换成另一个人使用CPU(或者执行此人后面的语句)。所以CPU的利用率是100%。第一个人I/O结束了,就要用事件来通知线程,执行回调函数。此时必须有事件环,就有一个排队调度机制,Node中有超过半数的C++代码,在搭建事件环。
Node.js和别的老牌3P不一样:
1) 没有自己的语法,使用V8引擎,所以就是JS。V8引擎解析JS的,效率非常高,并且V8中很多东西都是异步的。Node就是将V8中的一些功能自己没有重写(别人做了,自己就站在巨人肩膀上),移植到了服务器上。
2) 没有web容器,就是安装配置完成之后,没有一个根目录。
汇编语言 暴露几个API => C语言 暴露几百个API => 高级语言 暴露更多API
系统中,80端口,就是默认的http端口,所以当没有端口号的时候就默认80端口。
(https是443)
之前写了一个静态服务器,但是其实不是很完善,虽然对mime类型有判断了,但是返回的都是200状态码。304状态码表示已经存在这个文件,实现的机制是用cookies来给文件打标记,302表示重定向。这些都没有实现.(顺带一提)
二、模块
模块的概念
·在Node.js中,以模块为单位划分所有功能,并且提供了一个完整的模块加载机制,这时的我们可以将应用程序划分为各个不同的部分。
不可能用一个js文件去写全部的业务,肯定要有MVC。
·狭义的说,每一个javascript文件都是一个模块,而多个JavaScript文件之间可以相互require,他们共同实现了一个功能,他们整体对外,又称为一个广义上的模块。
·Node.js中,一个JavaScript文件中定义的变量、函数,都只在这个文件有效。当需要从模块外部引用这些变量、函数时,必须使用exports对象进行暴露。使用者要用require()命令引用这个JS文件。
var msg = ‘你好’
var info = ‘呵呵’
exports.msg = msg
msg这个变量是一个js文件内部才有作用域的变量。
使用者:
var foo = require(‘./test/foo.js’)
console.log(foo.msg);
暴露与引用。js文件中可以用exports暴露很多东西,比如函数、变量。
相当于增加了顶层变量。所有的函数、变量都要从这个顶层变量走。
Node中,js文件和js文件,就是被一个个exports和require构建称为网状依赖的。
不是像html文件中从上往下依赖的。
在js中可以将一个类暴露出去,然后进行引用。
a.js
function People(name,sex,age)
{
this.name = name;
this.sex = sex;
this.age = age;
}
People.prototype = {
SayHello:function(){
console.log(this.name+this.sex+this.age);
}
}
//直接暴露出来
module.exports = People;
b.js:
var People = require(‘./a.js’);
var xiaoming = new People(‘xiaoming’,12,’男’);
xiaoming.sayHello();
文件夹模块和package文件
如果require(‘a.js’)这样不写出路径
会自动默认的加载node_modules文件夹中的内容。
如果当前层没有,他就逐级遍历哪一层有Node_modules文件夹。
甚至可以放到NODE_PATH环境变量的文件夹中。
计算机-高级系统设置-环境变量-新建系统遍历 名字叫NODE_PATH。
·也可以使用文件夹来管理模块,比如:
var bar = require(‘bar’)
那么Node.js将会去寻找node_modules目录下的bar文件夹中的Index.js去执行。
可以自定义这个文件,需要建立package.json然后为main项设定。
这样require(‘bar’)文件夹的时候,就会去读取这个app.js
NPM
我们刚才学习了,模块就是一些功能的封装,所以一些成熟的、经常使用的功能,都有人封装成为了模块。
去社区搜索需求,然后点进去看api
..
- 我们的依赖包,可能在随时更新,我们永远想保持更新。
- 项目越来越大的时候,给别人看的时候,没有必要再次共享我们引用的第三方模块。
所以我们用package.json来管理我们的依赖。
package.json的
‘dependency‘中的^符号确保更新时这一位版本不变,其他的位数在install时候会自动更新。因为社区开发者每个人对自己的版本更新都有不同的定义。有时虽然大版本没变,但是小版本号的更新也可能带来了巨大的变化。会导致程序挂掉。所以出现了packagelock.json来锁定到特定的版本。
路径
文件的操作一定要用绝对路径,因为如果在a.js中require了一个b.js,这个js中有用fs读取了一个相对路径,那么读取出来的路径会有错误,因为node a.js的时候是以a为相对目录的。
require()别的文件的时候,会执行这个js文件。
实际上他会做一次绑定,把对象中的export对象绑定给承接的变量。
注意:require()中的路径是从当前js文件出发找到别人。所以,桌面上有一个a.js,test文件夹中有一个b.js和c.js
a要引用b:
var a = require(‘./test/b.js‘);
b要引用c:
var b = require(‘./c.js’)
但是,fs等其他的模块用到路径的时候都是相对于入口文件的。
所以,如果test文件夹中,有一个1.txt,那么在b.txt中想读取这个文件,请使用绝对路径。
(使用__dirname)
狭义的讲,一个js文件就是一个模块。
广义的讲,一个文件夹(或者是带package.json的文件夹)也是一个模块,因为会去读取文件夹内的index.js(或者package.json中的main:’入口文件.js’)
三、post请求
post请求
示例:
(req.addListener == req.on)
a.js:
const http = require(\'http\');
const querystring = require(\'querystring\');
var server = http.createServer((req, res) => {
//如果你的访问地址是这个,并且请求类型是post
if (req.url == \'/dopost\' && req.method == \'POST\') {
var alldata = "";
//不断的往事件环里去加载这个事件
//接受了一小段,可能就给别人去服务了。防止一个过大的表单阻塞了整个线程
req.on(\'data\', (chunk) => {
alldata += chunk;
});
req.on(\'end\', (chunk) => {
var datastring = alldata.toString();
res.end(\'success\');
//将datastring转为一个对象
var dataObj = querystring.parse(datastring, null, null,
);
console.log(dataObj);
console.log(dataObj.name);
console.log(dataObj.sex);
})
}
});
server.listen(3000);
aa.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://127.0.0.1:3000/dopost" method="POST">
<p>
姓名:
<input type="text" name=\'name\'>
</p>
<p>
性别:
<input type="radio" name=\'sex\' value=\'女\' id=\'female\'>
<label for="female">女</label>
<input type="radio" name=\'sex\' value=\'男\' id=\'male\'>
<label for="male">男</label>
</p>
<p>
爱好:
<input type="checkbox" name=\'hobby\' value=\'睡觉\'>睡觉
<input type="checkbox" name="hobby" value="吃饭">吃饭
<input type="checkbox" name="hobby" value="足球">足球
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
如果这里有一个图片上传的input 最后alldata.toString()以后会是undefined ,所以,对于图片要做一个二进制的文件的处理。
formidable
formidable是一个node.js的表单模块
看以下npm.js网址上对这个模块的介绍:
Purpose
A Node.js module for parsing form data, especially file uploads.
This module was developed for Transloadit, a service focused on uploading and encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from a large variety of clients and is considered production-ready.
(图为API的使用)
可以看到,把所有请求的表单域用fields(字段)表示,把所有的文件用files(文件)来表示。
aa.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://127.0.0.1:3000/dopost" enctype="multipart/form-data" method="POST">
<p>
姓名:
<input type="text" name=\'name\'>
</p>
<p>
性别:
<input type="radio" name=\'sex\' value=\'女\' id=\'female\'>
<label for="female">女</label>
<input type="radio" name=\'sex\' value=\'男\' id=\'male\'>
<label for="male">男</label>
</p>
<p>
爱好:
<input type="checkbox" name=\'hobby\' value=\'睡觉\'>睡觉
<input type="checkbox" name="hobby" value="吃饭">吃饭
<input type="checkbox" name="hobby" value="足球">足球
</p>
<p>
图片:
<input type="file" name=\'tupian\'>
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
12.js:
//formidable
1.js:
const http = require(\'http\');
const querystring = require(\'querystring\');
const formidable = require(\'formidable\');
const util = require(\'util\');
var server = http.createServer((req, res) => {
if (req.url == \'/dopost\' && req.method == \'POST\') {
//create a new incoming form
var form = new formidable.IncomingForm();
//设置文件上传存放的地址
form.uploadDir = "./uploads";
//执行里面回调函数的时候,表单已经全部接收完毕了。
form.parse(req, function (err, fields, files) {
if (err) {
throw err;
}
//所有的文本域、单选框,都在fileds里面存放;
//所有的文件与,都在files内存放。
//util.inspect 相当于console.log这个对象
console.log(util.inspect({ fields: fields, files: files }));
res.writeHead(200, { \'content-type\': \'text/html;charset=UTF-8\' });
res.write(\'received upload:\\n\\n\');
res.end(\'Success\');
});
return;
}
}).listen(3000);
在这节课学到的一个比较重要的东西是如果上传图片的时候,在form标签里一定要加enctype="multipart/form-data"
上传改名
原生的POST比较复杂,要写两个监听。
所以用第三方的formidable比较方便。
下面继续上面的案例来做,上传的文件名改名为2018050812323(时间+随机数)这样。
这里顺带介绍了一下silly-datetime插件 可以格式化的生成时间
https://www.npmjs.com/package/silly-datetime
(非常好用)
formidable里面生成的fields与files的数据结构是这样的:
13.js:
//上传图片 文件名是当前时间
//formidable
const http = require(\'http\');
const querystring = require(\'querystring\');
const formidable = require(\'formidable\');
const util = require(\'util\');
const fs = require(\'fs\');
const sd = require(\'silly-datetime\');
const path = require(\'path\');
var server = http.createServer((req, res) => {
if (req.url == \'/dopost\' && req.method == \'POST\') {
//create a new incoming form
var form = new formidable.IncomingForm();
//设置文件上传存放的地址
form.uploadDir = "./uploads";
//执行里面回调函数的时候,表单已经全部接收完毕了。
form.parse(req, function (err, fields, files) {
if (err) {
throw err;
}
//所有的文本域、单选框,都在fileds里面存放;
//所有的文件与,都在files内存放。
//util.inspect 相当于console.log这个对象
console.log(util.inspect({ fields: fields, files: files }));
//时间,使用的第三方模块,silly-datetime
var ttt = sd.format(new Date(), \'YYYYMMDDHHmm\');
var ran = parseInt(Math.random() * 89999 + 10000);
var extname = path.extname(files.tupian.name);
//执行该名
var oldpath = __dirname + \'/\' + files.tupian.path;
//新的路径由三个部分组成: 时间戳、随机数、扩展名
var newpath = __dirname + \'/uploads/\' + ttt + ran + extname;
//改名
fs.rename(oldpath, newpath, (err) => {
if (err) throw Error(\'改名失败\');
res.writeHead(200, { \'content-type\': \'text/html;charset=UTF-8\' });
res.end(\'成功\');
});
});
} else if (req.url == \'/\') {
//呈递form.html页面
fs.readFile(\'./aa.html\', (err, data) => {
res.writeHead(200, { \'Content-type\': \'text/html;charset=UTF-8\' });
res.end(data);
});
} else {
res.writeHead(404, { \'Content-type\': \'text/html;charset=UTF-8\' });
res.end(\'404\');
}
}).listen(3000);
aa.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://127.0.0.1:3000/dopost" enctype="multipart/form-data" method="POST">
<p>
姓名:
<input type="text" name=\'name\'>
</p>
<p>
性别:
<input type="radio" name=\'sex\' value=\'女\' id=\'female\'>
<label for="female">女</label>
<input type="radio" name=\'sex\' value=\'男\' id=\'male\'>
<label for="male">男</label>
</p>
<p>
爱好:
<input type="checkbox" name=\'hobby\' value=\'睡觉\'>睡觉
<input type="checkbox" name="hobby" value="吃饭">吃饭
<input type="checkbox" name="hobby" value="足球">足球
</p>
<p>
图片:
<input type="file" name=\'tupian\'>
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
这个项目可以运行在服务器上,运行在局域网中,然后让别人给你的电脑上传文件,很棒。
四、模板引擎
ejs模板引擎
<a href="<%=url%>"><img src="<%=imageURL%>" alt=""></a>
数据绑定,就成为一个完整的html字符串了。
前台的模板,我们现在要学习的是后台的模板。
后台模板,著名的有两个,第一个叫做ejs,第二个叫做jade
是npm第三方的包
先说ejs
Embedded JavaScript templates
(嵌入式javascript模板引擎)
index.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>好高兴啊,今天我买了一个iphone
<%= a %>s</h1>
<ul>
<%for(var i = 0;i<news.length;i++){%>
<li><%= news[i]%></li>
<% }%>
</ul>
</body>
</html>
1.js:
const fs = require(\'fs\');
const ejs = require(\'ejs\');
const http = require(\'http\');
var server = http.createServer((req, res) => {
fs.readFile(\'./views/index.ejs\', (err, data) => {
//绑定模板
var template = data.toString();
var dictionary = {
a: 6,
news: [21, 4, \'哈哈\']
};
var html = ejs.render(template, dictionary);
res.writeHead(200, { \'Content-type\': \'text/html;charset=UTF8\' });
res.end(html);
});
}).listen(2888);
(模板引擎这种在服务器上把语句组织起来的形式将逐渐被淘汰了,因为代码组织起来太杂糅了。这种时代一去不复返了。)
(未来后台只负责出数据)
jade模板引擎
ejs效率不高,因为是对字符串的处理 <% %>这种东西 下面介绍一下jade模板引擎
http://jade-lang.com/ (jade:玉)
www.npmjs.com/package/jade
(这个语法有点像Python)
这里不做介绍啦.
以上是关于Node_进阶_2的主要内容,如果未能解决你的问题,请参考以下文章