Node.js入门教程
简介
- nodejs是一个js运行环境runtime 让js可以开发后端程序 几乎能实现其他后端语言能实现的所有function
- nodejs基于googlev8引擎 原用于chrome的js解释部分,但后来被搬到服务器上用于做服务器的软件
- 优点 处理高并发 可以实现的功能多
搭建环境
- 略
第一次运行nodejs
- 建立app.js,然后console一段信息,打开终端输入node app.js(确保你的终端打开路径正确),有输出则为配置成功
创建第一个应用
http模块
创建
1
const http = require('http');
用于创建服务器
1
2
3
4
5
6
7
8
9
10
11http.createServer((req,res)=>{
设置响应头
状态码是200 文件类型是html 字符集是utf-8
res.writeHead(200,{"Content-type":"text/html;charset=utf-8"});
解决乱码
res.write("<head><meta charset='UTF-8'></head>");
必写结束响应
res.end();
}).listen(3000);端口
箭头函数是es6新增的特性和function差不多
url模块
创建
1
const url = require('url');
获取地址栏里面的其他属性 解析url get可以用 post要用第三方中间件
1
2
3url.parse(req.url,true).query;
true转换为一个对象
返回一个json数组类型
热刷新supervisor
- 输入cnpm找到对应网址进行安装指令 注意权限问题
- 再安装supervisor 注意权限问题
- 然后 将vscode 的默认终端从powershell转化成cmd
- 接着 输入supervisor app.js
- 便可以进行热更新 每次保存都会重启对应的js
- 注意如果报错会一直反复运行
commandjs 自定义模块
将部分功能性js封装到一个模块里面并且使用 的一种方式
下面以模块tools.js为例 看index.js如何调用模块的方法并使用
1
2
3
4
5>>tools.js
参见nodejs学习/demo3/module/tools.js
>>command02.js
参见nodejs学习/demo3/command02.js暴露方法 分为三类
1
2
3单个方法暴露
一个对象多个方法暴露
直接暴露
node_modules
- nodejs的标准库,里面放module的js
- 在导入的时候可以直接require(‘模块名/模块.js’),其中.js可以省略
生成package.json
- 读取路径的时候默认是读取node_modules下面的模块的index.js,而如果想要省略的写法 即直接读取单个模块下面单个js的路径 可以使用package.json将下面的单个js路径改为默认路径
- 方法 找到模块文件路径下的单个js 在地址栏输入cmd打开cmd窗口
- 之后输入 npm init —yes 就可以生成相关的package.json
第三方模块导入
用md5加密模块举例
- 网址里面下包
- npm install md5 —save 这样别人就知道你项目里面导入了哪些依赖,然后使用 npm i就可以导入依赖
- var md5 = require(‘md5’);
- md5(‘123132’);
关于依赖
- 如下
npm指令
- npm -v 看npm版本号
- npm -i 导入依赖
- npm install xx —save 导入xx包并将依赖文件保存
- npm uninstall xx
- npm list 查看当前目录下已经安装的node包
- npm info 模块 查看模块的版本
- npm install xxx@1.2.0 安装指定版本的xx包
- npm init 或者 npm init —yes 生成package.json
fs模块
1.fs.stat 检测是文件还是目录
2.fs.mkdir 创建目录
3.fs.writeFile 创建写入文件
4.fs.appendFile 追加文件
5.fs.readFile 读取文件
6.fs.readdir 读取目录
7.fs.rename 重命名
8.fs.rmdir 删除目录
9.fs.unlink 删除文件
数据流 管道流
数据流读取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const fs = require('fs');
var str = '';
var count = 0;
var readStream = fs.createReadStream('./data/output.txt');
readStream.on('data',(data)=>{
str+=data;
count++;
})
readStream.on('end',()=>{
console.log(str);
console.log(count);
})
readStream.on('error',(err)=>{
console.log(err);
})数据流写入数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 写入数据到文件流
const fs = require('fs');
var str = '';
for(var i=0;i<500;i++){
str+='数据流xxxxxxxxxxxxx';
}
var writeStream = fs.createWriteStream('./data/output.txt');
writeStream.write(str);
// 标记写入完成
writeStream.end();
writeStream.on('finish',()=>{
console.log('写入完成');
})管道流 复制文件之类的方法
1
2
3
4
5
6
7
8// 管道流 可以进行数据交换
const fs = require('fs');
var readStream = fs.createReadStream('./demo8.zip');
var writeStream = fs.createWriteStream('./data/demo8.zip');
readStream.pipe(writeStream);
异步
async 异步函数 await 等待异步函数执行完毕
同步就是根据代码全部流程一步一步执行
而异步就是遇到异步函数 先将其放到任务栈 然后等待所有其他函数执行完毕 之后再执行异步函数
关于js的异步同步执行顺序
1
2
3
4流程如下
先执行同步
再执行全部微任务
再执行第一项宏任务延迟函数类似于异步
异步的一个例子
1
2
3
4
5
6
7
8
9
10
11
12
13async function test(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
var name = 'zhangsan222';
resolve(name);
},1000);
})
}
async function main(){
var data = await test();
console.log(data);
}
main();
利用http模块,fs模块,url模块,path模块 创建一个静态web服务器
http模块
首先一个网页的基本请求和响应需要用到http模块
导入http模块 创建服务器
设置响应头
1
2
3
4res.writeHead(200,{
"Content-Type":"text/html;charset='utf-8'"
})
注意设置状态码和文件类型注意写结束响应 和 端口
fs模块
- 作用是读取本地中的静态资源
- 一般用到fs.readFile
- 注意写err
path模块
作用是将地址中的后缀名给分离出来 (此时使用到path.extname()),传给getMime函数
getMime函数的作用是根据后缀名 返回相应的文件类型
1
2
3
4
5
6
7
8
9exports.getMime = ((extname)=>{
switch (extname){
case: '.html':
return 'text/html';
case: '.css':
return 'text/css';
case: ....
}
})
url模块
通过url.parse()方法解析req.url里面的pathname
原因是使用json的时候 getMime无法将他返回相关的文件类型
使用json的时候 不需要地址栏后面的数字
所以
1
let pathname = url.parse(req.url).pathname;
command.js
- modules下面新建的js 暴露一个getFIleMime方法使其根据path模块的extname方法传来的后缀名 返回相应的文件类型
- 自定义 自定义比较有限
- 通过mime.json文件(该json文件内部写了很多的文件类型 以键值对的形式存在
那么就需要读取mime.json文件 需要用到fs模块中的readFile方法
- 如果使用../data/mime.json’会找不到该json文件 原因是他是基于app.js的路径来读取的 所以应该使用./data/mime.json
- 其次 输出readFile里面的data值的时候 直接输出是一个buffer流十六进制 需要将其转换成String 此处使用到data.toString()
- 之后 我们的根本目的是通过json数组键值对 获取对应的文件类型 所以还要将得到的String字符串转换成json数组 此处使用到 JSON.parse(STRING)
- 接着 将json数组传递给Obj类型变量举例mimeObj 但此时如果直接打印mineObj.extname是无法识别的 因为变量属性名只能通过方括号的形式访问 此处用到 mimeObj[extname]
- 然后 因为fs.readFile是异步方法 不能直接获取到值 需要使用promise来封装 此处使用到return new Promise((resolve,reject)=>{ 异步函数 }),使用里面的参数resolve输出
- 最后 app.js中调用该方法的时候 注意在接收传递来的值之前使用await 并且记得await需要在异步函数中使用 即函数要加async
- 或者使用fs模块里面的readFileSync方法 同步读取方法
Ejs模板引擎
ejs属于后台模板 可以将数据库和文件读取的数据显示到html页面上,他是一个第三方模块 需要用npm安装
npm install ejs —save
nodejs中使用
1
2
3
4
5
6ejs.renderFile('./views/login.ejs',{
msg:msg
传入的数据
},(err,data)=>{
回调内容一般必写响应头和结束响应
})ejs里面传参的方法
1
2
3
4
5
6
7假设需要传入msg给h3标签
<h3><%=msg%></h3>
假设需要用到for循环传输一个obj数组到li中
<%for(var i=0;i<obj.length;i++){%>
<li><%=obj.属性%></li>
<%}%>
Get Post Put Delete
获取get传值
1
2const url = require('url');
vargetData = url.parse(req.url).query;获取post传值
1
2
3
4
5
6
7
8
9
10
11
12var postData = '';
req.on('data',(chunk)=>{
postData+=chunk;
});
req.on('end',()=>{
try{
postData = JSON.parse(postData);
}catch(e){
req.query = postData;
console.log(queryString.parse(postData));
}
})put主要用于修改数据 可以用postman软件模拟
delete主要用于删除数据
Express框架
简单来说就是一个路由框架 封装了很多路由方法
引入
1
var express = require('express');
实例化框架 然后通过app.get / app.post 配置路由
1
2
3
4
5
6
7var app = express();
app.get("/",(req,res)=>{
res.send("hello world");
})
app.post("/",(req,res)=>{
res.send("hello world");
})具体封装的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39// 4定义一个全局变量
let G = {};
let app = function (req, res) {
// console.log('调用app方法');
if (G['/login']) {
// console.log("1");
// console.log(G['./login']);
G['/login'](req, res); //6执行方法
}
}
/*
1现在使用express的方法 并理解其原理
*/
// 3注册
app.get = function (str, cb) {
//5注册方法 将回调函数传给str
G[str] = cb;
// console.log(str);
// console.log(cb);
}
// app.get("/login",(req,res)=>{
// // res.send('hello world');
// console.log('执行login方法');
// })
app.get("/login", function (req, res) {
// res.send('hello world')
console.log("执行login方法")
})
// 2要想使用这个方法 需要给他绑定方法
// 7
setTimeout(() => {
app('req', 'res');
}, 1000);支持多级路由配置方式
1
2
3app.get("/admin/user/add",(req,res)=>{
})动态路由 可以获取下一级目录的名字 当匹配到改动态路由的时候,不会再往下继续匹配
1
2
3
4app.get("./article/:id",(req,res)=>{
var id = req.param["id"];
res.end("动态路由"+id);
})配置路由的时候还需要注意顺序的问题 以静态路由优先 随后配置动态
获取get传值
以前是要通过url模块的url.parse(req,url).query来获取 很麻烦
现在可以直接使用req.query
1
2
3
4
5app.get("/product",(req,res)=>{
let query = req.query;
console.log(query);
res.end("product");
})
express中使用ejs
安装
1
cnpm install ejs --save
配置模板引擎
1
app.set("view engine","ejs");
express中已经集成了ejs 所以不用引入
默认在views文件夹下面,但也要自己新建一个
使用的时候
1
2res.render("ejs名字",{传入的数据});
ejs名字可以不用写后缀绑定数据
1
<%=shuju%>
输出内容的时候解析其中的 html 标签
1
<%-<h3>lebpig</h3>%>
条件判断
1
<%语句%>
在一个ejs里面引入其他ejs
ejs后缀修改为html 略
利用express.static托管静态文件
如果你的静态资源存放在多个目录下面 可以多次调用express.static 中间件
1
app.use(express.static('public'));
虚拟静态目录 略
中间件
简介
- 通俗的讲就是匹配路由之前或者匹配路由完成所做的一系列操作 中间件如果想向下匹配的化 需要些 next()
express可以使用如下几种中间件
- 应用级中间件
- 路由级中间件
- 错误处理中间件
- 内置中间件
- 第三方中间件、
其中讲解部分
内置中间件 之前学的托管静态文件↑
应用级别中间件 通常用于权限判断
1
2
3
4app.use(function(req,res,next){
//操作
next(); //表示完成匹配后这个中间件之后的程序继续向下执行 如果不写 会停止
})错误处理中间件
1
2
3
4app.use((req,res,next)=>{
res.status(404).send("404");
})
当页面没有对应的路由的时候 跳转404第三方中间件 获取post传值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
161.安装body-parser
cnpm install body-parser --save
2.配置
const bodyparser = require('body-parser');
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(bodyParser.json());
3.使用
app.post('/doLogin', (req, res) => {
var body = req.body;
res.writeHead(200,{
"Content-type":"text/html;charset=utf-8"
})
res.end('执行提交'+body.username);
})
Cookies
安装
1
cnpm install cookie-parser --save
配置中间件
1
app.use(cookieParser())
设置cookie
1
2
3
4
5
6
7res.cookie("username","zhangsan",{maxAge:1000*60*60})
//代表获取了username张三 并且存放一小时
//maxAge是最大存放时间
res.cookie("username","zhangsan",{maxAge:1000*60*60,path:"/article"})
//path代表只有xxx能获取cookie
{domin:'.itying.com'}
//domin可以实现多域共享cookiecookie的加密
1
2
3
4
5
6//配置中间件的时候传入加密的参数
app.use(cookieParser("suibianxie"));
//写他的属性
res.cookie("username","zhangsan",{maxAge:1000*60*60,signed:true})
//需要得到他的时候 写
req.signedCookies
Session
简介
- session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而session保存在服务器上
- session相比cookie要更加安全,由于session保存到服务器上,所以当访问量增多的时候,会比较占用服务器的性能。单个cookie保存的数据大小不能超过4k,很多浏览器限制一个站点最多保存20个cookie。而session没有这方面的限制。session是基于cookie进行工作的。
session的工作流程
- 当浏览器访问服务器并发送第一次请求的时候,服务器会创建一个session对象,生成类似于key value的键值对,然后将key(cookie)返回到浏览器中,浏览器下次再访问时候,携带key(cookie)找到对应的session
express-session的使用
安装
1
cnpm install express-session --save
引入
1
var session = require('express-session');
设置官方文档提供的中间件
1
2
3
4
5
6
7
8
9
10
11app.use(session({
secret:'keyboard cat', //服务器端生成session的签名
name: 'xxx', //修改session对应cookie的名称
resave: true, //强制存储session 即使它没有变化 默认是false
saveUninitialized: true, //强制将未初始化的session存储
cookie:{
maxAge: 1000*60*60,
secure: false //true表示只有https协议才能访问cookie
},
rolling:true //在每次请求时强制设置cookie 将重置cookie过期时间,默认false
}))使用
1
2设置值 req.session.username = 'zhangsan';
获取值 req.session.username;
负载均衡配置
- 分布式架构中session保存到数据库的流程
如何使用
安装express-session 和 connect-mongo模块
引入模块
1
2var session = require('express-session');
const MongoStore = require('connect-mongo')(session);配置中间件
1
2
3
4
5
6
7
8
9
10
11
12
13
14app.use(session({
name: 'xxx', //修改session对应cookie的名称
resave: true, //强制存储session 即使它没有变化 默认是false
saveUninitialized: true, //强制将未初始化的session存储
cookie:{
maxAge: 1000*60*60,
secure: false //true表示只有https协议才能访问cookie
},
rolling:true //在每次请求时强制设置cookie 将重置cookie过期时间,默认false
store: new MongoStore({
url: 'mongodb://127.0.0.1:27017/xxx' //xx代表地址 可以直接用cmd输入mongo找到mongodb对应的地址
touchAfter: 24*3600 //不管发出了多少次请求 24h内只更新一次session 除非你改变session
})
}))
路由模块化
简介
- express中允许我们通过express.Router创建模块化的 可挂载的路由处理程序
应用程序生成器
简介
- 通过应用程序生成器可以快速创建一个应用的股价
指令
如下
1
cnpm install -g express-generator
验证安装成功
1
express -h
生成方法
1
express --view=ejs 目录
图片上传Multer
简介
- 是nodejs一个中间件,用于处理multpart/form-data类型的表单数据,主要用于上传文件
- 注意它不会处理除了上述数据类型的数据
- 一般上传图片识别不了 所以需要用到它
安装
指令
1
npm install --save multer
引入模块
1
2
3var multer = require('multer');
var upload = multer({dest:'static/upload'});
//上传目录 一定要存在配置
1
2
3
4
5
6
7router.post("/doAdd",upload.single("pic"),(req,res)=>{
xxx
})
//此处pic是对应的表单的name
//并且form表单要设置类型
<form enctype='multpart/form-data'>
MongoDb数据库
NoSql介绍
是not only sql的意思 指的是非关系型数据库 是以key-value形式存储的数据库 键值对
解决大数据查询问题
和传统数据库的简单对比
1
2非结构型数据库 没有行列的概念 用json来存储数据,集合相当于“表” 文档相当于“行”
图例
MongoDb介绍
- 是介于关系型数据库和非关系型数据库之间的产品 他 的查询语言非常强大,几乎可以实现类似于关系型数据库表单查询的大多数功能
- 特点是高性能 易部署 易使用 存储非常方便
- 安装略
- 注意配置环境变量
- cmd输入mongo可以开启
增删改查
创建数据库 已经创建则切换到这个数据库,如果想要创建成功需要插入一条数据
1
2use xxx
db.user.insert({"name":"leb"});查看数据库
1
show dbs
插入数据
1
db.user.insert({"name":"leb"});
删除集合
1
db.user.drop();
删除数据库 需切换到
1
db.dropDatabase();
查找数据
1
db.user.find();
查找去掉当前聚集集合中的某列的重复数据
1
db.user.distinct("xxx");
查询age =22 的记录
1
db.user.find({age:22});
大于22 小于22 大于等于 小于等于
1
2
3
4db.user.find({age:{$gt:22}});
db.user.find({age:{$lt:22}});
db.user.find({age:{$gte:22}});
db.user.find({age:{$lte:22}});大于22小于26
1
db.user.find({age:{$gt:22,$lt:26}});
查询name中包含mongo的数据 模糊查询用于搜索
1
db.user.find({name:/mongo/});
查询指定列name age数据,age>25
1
2
3db.user.find({age:{$gt:25}},{name:1,age:1})
相当于
select name,age from user where age>25按照年龄升序 降序
1
2
3
4升序
db.user.find().sort({age:1});
降序
db.user.find().sort({age:-1});查询前五条数据
1
db.user.find().limit(5);
查询十条以后的数据
1
db.user.find().skip(10);
修改数据
1
db.student.update({"name":"小明" })
删除数据
1
2db.collectionsNames.remove({"borough":"xxzxc"})
db.users.remove({age:132})
索引基础
创建索引
1
db.user.ensureIndex({"username":1})
获取当前集合的索引
1
db.user.getIndexes()
删除索引的命令
1
db.user.dropIndex({"username":1})
Nodejs操作Mongodb
安装指令
1
npm install mongodb --save
连接数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 引入mongodb
const {MongoClient} = require('mongodb');
// 定义数据库连接地址
const url = 'mongodb://127.0.0.1:27017';
// 定义要操作的数据库
const dbName = 'itying';
// 实例化MongoClient 传入数据库连接地址
const client = new MongoClient(url,{useUnifiedTopology:true});
// 连接数据库
client.connect((err)=>{
if(err){
console.log(err);
return;
}else{
console.log('sjk connect successful');
let db = client.db(dbName);//切换到这个数据库
client.close();//关闭数据库
}
})查找数据
1
2
3
4db.collection("user").find({"name":"leb"}).toArray((err,data)=>{
console.log(data);
client.close();//关闭数据库
})增加数据
1
2
3
4
5
6
7
8
9
10db.collection("user").insertOne({"name":"zzl"},(err,result)=>{
if(err){
console.log(err);
return;
}else{
console.log('add successful')
console.log(result);
client.close();//关闭数据库
}
})修改数据
1
2
3
4
5
6
7
8
9db.collection("user").updateOne({"name":"leb"},{$set:{"name":"lebpig"}},(err,data)=>{
if(err){
return console.log(err);
}else{
console.log('modify successful');
console.log(data);
client.close();//关闭数据库
}
})删除数据
1
2
3
4
5
6
7
8
9db.collection("user").deleteOne({"name":"zzl"},(err,data)=>{
if(err){
return console.log(err);
}else{
console.log('delete successful');
console.log(data);
client.close();
}
})