node.js学习笔记
node.js学习笔记
node
01
什么是node?
- chrome V8 runtime
- 事件驱动
- 非阻塞的i/o
优点:处理高并发比较好
模块化
- 内置模块(node中自带的模块)
- 文件操作
- fs.readdir
- fs.mkdir(同步,异步)
- fs.rename
- fs.rmdir(只能删除空文件夹)
- 错误信息处理 node中大多数秉持错误优先原则,回调中有err参数,同步代码为了不影响后续代码执行需要使用try {} catch (err) {}捕获错误。
- 第三方模块
- 自定义模块
自定义模块使用模块步骤
1.创建一个模块(一个js文件一个模块)
2.导出一个模块(module.exports = name)
3.引入一个模块并且调用
运行环境
REPL :在命令行执行的环境。
内置文件模块fs
文件操作
创建文件(覆盖写入)
fs.writeFile(path,option,callback)
写入文件(追加写入)
fs.appendFile(path,option,callback)
读取文件
fs.readFile(path,config,(err,msg)=>{
//msg默认为buffer数据流 可以使用 msg.toString('utf8')转换数据
//也可以使用config出配置‘utf8’
})
删除文件
fs.unlink(path,callback)
内置url模块
url : 统一资源定位符
url.parse();将url字符串转换为对象
url.format();将url对象转换为字符串
内置querystring模块
qs.parse(str,query1,query2);
//query1 表示以什么符号切分键值对 默认 &
//query2 表示以什么符号切分键与值 默认 =
qs.stringify(obj,query1,query2);
//query1 表示以什么符号拼接键值对 默认 &
//query2 表示以什么符号拼接键与值 默认 =
qs.escape() //编码
qs.unescape() //解码
第三方模块
nodemailer 可以实现发邮件
"use strict";
const nodemailer = require("nodemailer");
// 创建一个发送器
let transporter = nodemailer.createTransport({
host: "smtp.qq.com",
port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: '572410049@qq.com', // 发送者邮箱地址
pass: 'pwuepzqfdpjybeid' // 发送者SMTP密码
}
});
// 配置发送信息对象
var emailconfig = {
from: '572410049@qq.com', // 发送者邮箱地址
to: "572410049@qq.com", // 接受者邮箱地址
subject: "你好", // 主题
text: "准备睡觉吧", // 内容 只能是纯文本 与 html 配置项只能存在一个
// html: "<b>Hello world?</b>" // html body
}
// 调用sendMail()方法
transporter.sendMail(emailconfig);
爬虫案例
1.获取目标网站
2.分析网站内容
3.获取有效信息 下载或其他操作
http 模块
http模块负责发请求:
const http = require('https');
let html = '';
http.get(url,(res)=>{
let { statusCode } = res; //状态码
let contentType = res.headers['content-type']; //请求类型
let err = null;
if(statusCode !== 200){
err = new Error('请求状态出错');
}else if(!/^text\/html/.test(contentType)){
err = new Error('请求类型出错');
}
if(err){
console.log(err);//此处可以确定错误类型
res.resume(); //重置缓存
return false //终止程序
}
//---------------------------- 代码健壮判断抓取的是否为所需类型,和是否能访问目标地址 ----------------------------------------
res.on('data',(chunk)={
//chunk 为buffer数据流 需通过chunk.toString('utf8')转换
//爬取中。。。
html += chunk.toString('utf8')
});
res.on('end',()=>{
console.log(html);
//爬取结束。
})
}).on('error',(e)=>{
console.log('请求出错')
})
cheerio 模块
将一组html格式的字符串 转化为类之后可以通过jq的语法选中其中的元素。
const cheerio = require('cheerio');
//将一组html格式的字符串 转化为类之后可以通过jq的语法选中其中的元素。
var div = '<div> <img src="http://www.baidu.com"/> </div>'
const $ = cheerio.load(div);
console.log($('img').attr('src'));
实现抓取一个页面的图片
const http = require('https');
const fs = require('fs');
const cheerio = require('cheerio');
const request = require('request');
const reg = /(http|https):\/\/([\w.]+\/?)\S*/ig
let url = 'https://www.zcool.com.cn/';
let html = '';
http.get(url, (res) => {
let { statusCode } = res; //状态码
let contentType = res.headers['content-type']; //请求类型
let err = null;
if (statusCode !== 200) {
err = new Error('请求状态错误')
//请求状态错误
} else if (!/^text\/html/.test(contentType)) {
//请求类型错误
err = new Error('请求类型错误')
}
if (err) {
console.log(err);
res.resume(); //重置缓存
return;
}
res.on('data', (chunk) => {
html += chunk.toString('utf8');
// console.log('爬取中')
});
res.on('end', () => {
console.log(html);
const $ = cheerio.load(html)
$('img').each((index, el) => {
if (reg.test($(el).attr('src'))) { //判断是否是以http 或者https 开头的
request($(el).attr('src')).pipe( //下载到本地
fs.createWriteStream(`./node/${index}.png`).on('close', err => {
console.log('下载成功', err)
})
)
}
})
})
}).on('error', (e) => {
console.log('出错啦');
})
02
通过express 框架书写api
1.安装 express
npm install express --save
模块(第三方模块)的引用 从当前目录的node_modules依次向上寻找。
服务器相关
服务器:
1.服务器本质就是一台电脑
2.需要服务器软件(apatch tomcat iis nginx node)
3.服务器ip和端口号 (80)
局域网:服务器通过(无线)网线连接 每一台电脑都被分配一个ip
外网:
ip:确定服务器主机的位置
port:是确定服务器里程序的位置
api接口
构成要素:ip port pathname method(get,post) data
- 接收数据
- get req.query
- post req.body 需要body-parser模块
- 注意数据格式
- json (app.use(bodyParser.json()))
- x-www-form-urlencoded (app.use(bodyParser.urlencoded(extended:false)))
- formdata
- 注意数据格式
中间件 middlewear
- 内置中间件 static
- 自定义中间件
- 第三方中间件 (body-parser)
中间件使用一定要注意 next);
中间件有全局中间件,还有局部中间件,局部中间只走该中间件所在接口。
static 静态目录 (类似apche www目录)
path 模块 处理路径
对文件操作 必须使用绝对路径
__dirname 绝对路径
static 模块使用
app.use('./',express.static(path.join(__dirname,'./html')));
此时 可以通过http://localhost:3000/index.html 访问html文件夹下的index.html文件
node router
防止一个文件写过多接口造成不良影响,拆分接口到对应文件
server.js
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use('/',(req,res,next)=>{
if(req.query.token || req.body.token){
next()
}else{
res.send('no token')
}
});
//引入各个router
const userRouter = require('./router/user');
const customRouter = require('./router/custom');
//使用各个router
app.use('/user',userRouter);
app.use('/custom',customRouter);
app.listen(3000,()=>{
console.log('server start');
})
router/user.js
const express = require('express');
const router = express.Router(); //实例化router
router.get('/login',(req,res)=>{
res.send('login ok')
})
router.get('/logout',(req,res)=>{
res.send('logout ok');
})
module.exports = router; //导出
非关系数据库 mongodb
1.mongodb 下载 (下载速度慢问题解决mongodb下载慢问题)
2.安装 建议参考菜鸟教程 mongodb安装 菜鸟教程mongodb windows安装
指令
mongodb 数据库名
mongod 命令行启动数据库
mongo 命令行操作数据库指令
mongoose node操作mongodb的插件
promise
为什么要是用promise?
大量的异步操作如果需要顺序执行通过回调函数会形成回调地狱
通过promise解决回调地狱
1.封装promise函数
//使用形式
//返回promise
function test(){
return new Promise((resolve,reject)=>{
//异步处理
//成功的时候
resolve();
//失败的时候
reject();
})
}
2.根据顺序,形成链式调用
test().then().then();
在第一个then中需再return出一个promise对象
3.捕获错误
示例代码:
const fs = require('fs');
//回调形式
// fs.stat('./aaa.js',(err,stats)=>{
// if(err){
// console.log('文件不存在')
// }else{
// console.log('文件存在');
// fs.unlink('./aaa.js',(err)=>{
// if(err){
// console.log('删除错误');
// }else{
// console.log('删除正确');
// }
// })
// }
// })
//promise形式
function fileIsExsit(){
return new Promise((resolve,reject)=>{
fs.stat('./aaa.js',(err,stats)=>{
if(err){
console.log('不存在')
reject('err')
}else{
console.log('存在')
resolve('yes')
}
})
})
}
function filedel(){
return new Promise((resolve,reject)=>{
fs.unlink('./aaa.js',(err)=>{
if(err){
reject('删除失败')
}else{
resolve('删除成功')
}
})
})
}
fileIsExsit()
.then((data)=>{
console.log(data,'------------then')
return filedel();
})
.then((data)=>{
console.log(data,'----then')
//如何手动终止then?
throw new Error('手动抛出错误,为了手动终止then执行');
})
.then((a)=>{
console.log(111);
})
.catch((data)=>{
console.log(data,'------------catch')
})
mongoose
mongoose:为了方便操作数据库而诞生的一个模块
1.安装mongoose:
npm install mongoose
2.引入模块,连接数据库
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/users',{ useNewUrlParser: true,useUnifiedTopology: true })
//第一个参数为本地数据库,第二个参数为mongoose.connect()方法新增的参数,否则会warning
3.创建数据库连接对象
var db = mongoose.connection;
db.on('error',console.error.bind(console,'connection error:')) //监听错误
db.on('open',function(){ //监听连接成功
console.log('数据库连接成功');
})
4.创建schema对象
var userSchema = new mongoose.Schema({
name:String,
age:Number,
sex:{
type:Number,
default:0
},
})
5.创建model结构
//将schema对象转换为数据模型
//将schema对象与集合关联('集合名',schema对象);注意:集合名mongoose会自动转换成复数形式因此建议集合名使用复数形式
var User = mongoose.model('users',userSchema);
6.操作数据库
//增
//insertMany([options]) options 可为数组 可为对象 是追加操作,会追加到当前表的数据末尾
// User.insertMany([{name:'tll',age:18},{name:'zt',age:18,sex:0},{name:'ah',age:18,sex:0}])
// .then((data)=>{
// console.log(data);
// console.log('插入成功');
// })
// .catch((data)=>{
// console.log('插入失败');
// })
// 删
// deleteOne([option]) 删除一条数据,option为筛选条件,当有多条数据符合该条件时,只删除第一个查到的,当option为空时默认删除第一条
// deleteMany([option]) 删除多条数据,option为筛选条件,当options为空时删除该集合所有数据
// User.deleteMany({name:'zt'})
// .then((data)=>{
// console.log(data,'删除所有成功')
// })
// .catch((data)=>{
// console.log(data,'删除所有失败');
// })
// 改
// update(query,update) query为查询条件,update为修改后的数据 如果匹配到多条数据只修改第一条
// updateMany(query,update) 批量修改query为查询条件,update为修改后的数据
// updateOne(query,update) 与update()相同,只修改匹配到的第一条数据
// User.update({sex:1},{age:90})
// .then((data)=>{
// console.log(data,'更新成功');
// })
// .catch((data)=>{
// console.log(data,'更新失败')
// })
// 查
/*
mongodb 条件操作符
$gt >
$lt <
$gte >=
$lte <=
$ne !=
$eq =
模糊搜索
{title:/教/} title中包含教的文档
{title:/^教/} title中以教开头的文档
{title:/教$/} title中以教结尾的文档
*/
//find(query) // 查询符合条件的所有数据
//findOne() //查询符合条件的第一条数据
//findById() //通过id查找一条数据,该id为mongodb自动生成的id 在查找前会对id进行数据转换
// User.findById("5e70dcf88963670128d1c20e")
// .then((data)=>{
// console.log(data,'查询成功')
// })
// .catch((data)=>{
// console.log(data,'查询失败')
// })
使用mongodb + nodeJs实现一个登录注册逻辑
1.项目结构
- server.js //服务
- db //数据库管理
- model
- userModel.js //创建userModel对象
- connect.js //连接数据库
- router //路由
- useRouter.js //接口具体逻辑
2.项目需求分析
1.注册
先判断数据库中是否有userName,有则返回账号已存在,注册失败,否则写入数据库,标识注册成功。
2.登录
根据userName 和 password 判断数据库中是否存在该条数据,存在则表示登录成功,否则表示失败。
server.js
const express = require('express');
const db = require('./db/connect');
const app = express();
const userRouter = require('./router/userRouter');
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false}))
app.use('/user',userRouter);
app.listen(3000,()=>{
console.log('server start');
})
connect.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/users',{ useNewUrlParser: true,useUnifiedTopology: true });
const db = mongoose.connection;
db.on('error',()=>{
})
db.once('open',()=>{
console.log('connect ok');
})
userModel.js
const mongoose = require('mongoose');
var userModel = new mongoose.Schema({
us:{
type:String,
default:''
},
ps:{
type:String,
default:'',
},
uid:{
type:String,
default:''
}
})
var user = mongoose.model('users',userModel);
module.exports = user;
userRouter.js
const express = require('express');
const router = express.Router();
const User = require('../db/model/userModel');
router.post('/reg',(req,res)=>{
let{us,ps} = req.body;
if(!us || !ps){
res.send({
err:-1,
msg:"参数错误"
})
}else{
User.find({us})
.then((data)=>{
if(data.length >= 1){
res.send({
err:-1,
msg:"该用户已注册"
})
}else{
return User.insertMany({us,ps,uid:us+ps})
}
})
.then((data)=>{
if(data){
res.send({
err:0,
msg:'注册成功'
})
}
console.log(data,'insert')
})
.catch((err)=>{
res.send({
err:-1,
msg:'系统内部错误,请稍后重试'
})
})
}
})
router.post('/login',(req,res)=>{
let {us,ps} = req.body;
if(!us || !ps){
res.send({
err:-1,
msg:'参数错误'
})
}else{
User.find({us,ps})
.then((data)=>{
console.log(data,'50000');
if(data){
res.send({
err:0,
msg:'注册成功',
us:data[0].us,
uid:data[0].uid
})
}
})
.catch((err)=>{
res.send({
err:-1,
msg:'系统内部错误'
})
})
}
})
module.exports = router;
跨域问题
ajax 同源策略 协议 域名 端口号
解决跨域:
- 1.cors:后端配合
- express cors
- app.all()
app.all("*",function(req,res,next){ //设置允许跨域的域名“*”代表允许所有
//允许的请求头
res.header("Access-control-Allow-Origin","*");
//设置允许的请求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if(req.method.toLowerCase() == 'options'){
res.send(200)
}else{
next();
}
})
上传图片
npm install multer --save
//后端
var storage = multer.diskStorage({
destination:function(req,file,cb){
cb(null,'./uploads'); //定义文件存放地址
},
filename:function(req,file,cb){ //定义上传的文件名
let types = file.originalname.split('.');
let type = types[types.length - 1]; //取后缀
cb(null,file.fieldname+'-'+Date.now()+parseInt(Math.random()*9999)+'.'+type);
}
})
var upload = multer({storage:storage}); //调用
router.post('/imgs',upload.single('keys'),(req,res)=>{ //这个keys 属于约定,前后端都得知道
const {mimetype,size,filename} = req.file;
let types = ['jpg','png','jpeg','gif'];
let imgtype = mimetype.split('/')[1];
if(size>102400){ //上传大小判断
res.send({
err:-1,
msg:'图片尺寸超过10k'
});
return;
}else if(types.indexOf(imgtype)<=-1){ //文件类型判断
res.send({
err:-1,
msg:"图片类型错误"
})
return;
}else{
res.send({
err:0,
msg:"上传成功",
url:`/img/${filename}` //上传成功后将地址返回给前端
})
}
})
//前端
$('button').click(function(){
let formData = new FormData();
let file = $('#file')[0].files[0];
formData.append('keys',file);
$.ajax('http://localhost:3000/upload/imgs',{
method:'POST',
cache:false,
data:formData,
processData:false,
contentType:false,
success:function(data){
console.log(data);
$('img').attr('src',`http://localhost:3000${data.url}`)
}
})
console.log(file);
})